From 5ef27955ad4b6ab2114d338c24e6b25224ee54ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A1n=20P=C3=A9rez?= Date: Wed, 7 May 2014 17:33:12 +0200 Subject: [PATCH 001/823] umd layers options: template draft --- .../images/backgrounds/map_options_tree-1.png | Bin 0 -> 535 bytes .../images/backgrounds/map_options_tree-2.png | Bin 0 -> 1079 bytes app/assets/images/icons/icon_options.png | Bin 0 -> 682 bytes .../images/icons/icon_options_active.png | Bin 0 -> 705 bytes .../images/icons/icon_options_disable.png | Bin 0 -> 627 bytes .../images/icons/icon_options_hover.png | Bin 0 -> 687 bytes app/views/shared/_js_templates.html.erb | 7 +- .../javascripts/gfw/ui/umd_options.js.erb | 274 ++++++++++++++++++ 8 files changed, 280 insertions(+), 1 deletion(-) create mode 100644 app/assets/images/backgrounds/map_options_tree-1.png create mode 100644 app/assets/images/backgrounds/map_options_tree-2.png create mode 100644 app/assets/images/icons/icon_options.png create mode 100644 app/assets/images/icons/icon_options_active.png create mode 100644 app/assets/images/icons/icon_options_disable.png create mode 100644 app/assets/images/icons/icon_options_hover.png create mode 100644 lib/assets/javascripts/gfw/ui/umd_options.js.erb diff --git a/app/assets/images/backgrounds/map_options_tree-1.png b/app/assets/images/backgrounds/map_options_tree-1.png new file mode 100644 index 0000000000000000000000000000000000000000..b32cdf3c0de726caad61a6350ddf82f3fefe5f78 GIT binary patch literal 535 zcmeAS@N?(olHy`uVBq!ia0vp^5IQ+eo=O@f^)Fhi#?lqfa*j*>YR&G z6H7Al^Atidb5j}2^-c8+O&)!o(#XKTDCOzm7@{#TIe~%knJ*8Ag1I-t!I}RrFq{*M ztY5`=?&076`OI?`|N1vyX^wLBzyGQ;IL}=CUw_=n!O{%q)!)$!PdoMk8TCw49xr5n zqT-nIXifF+|Npn!bXZNh{kzfVzv8jNhAn^oGY1}Hc^1=pV#n1+3*MlF*(nZh{x6Sf zPCeIP`Cormv&HL!Z|Vc%8{gdfA7H?E!tyq&+1vluV;I@K?F{JLzQ1+BwuS>2wRv>@ z8l2mi&m;5y;R8)(XZfDL>DtWhCo46czgK4SYuUu=F{ggD#128De#x$csDJ{Y z)BD`ac<|r)0|&Uu&j}~oSa*+kW2%;gOO4~3-Qfz1S~1^^swYa>c(GWBDYzOOXG=N9 gs>1i1-GG(h+v9)!hq(1W14EI))78&qol`;+03l297XSbN literal 0 HcmV?d00001 diff --git a/app/assets/images/backgrounds/map_options_tree-2.png b/app/assets/images/backgrounds/map_options_tree-2.png new file mode 100644 index 0000000000000000000000000000000000000000..177313a5ccef8e03cc2bf45d6252d3efb5f81def GIT binary patch literal 1079 zcmeAS@N?(olHy`uVBq!ia0vp^(Lij&!VDyTzr5`Mq?n7HJVQ7*IBq}me*okf1o(uw z{{R2~3@PCG`4=~UdId^?{DK(-m5g0N)5<$$uG)R(?z{hGQyE2pa-0Pok;M!Q+(IDC zcIQ+eo=O@f^)Fhi#?lqfa*j*>YR&G z6H7Al^Atidb5j}2^-c8+O&)!o(g^f|m8Xkih(~Ad)L4%YN0El~>wg`CBVH*U>@hXi zpy9iCxx-qy9s%VS&lehwFaFOeARKHo(5mm*)E|Z%% z^+cw;joP&BrrsY!xAb$&u~9#9WD?I@5BC3kLGL-rXQ+hpYI64e^K9>qKDl<=`{iEE zzmqp^G|4TOv=F>{Rud4WL|-F`P8{=ahWbU@$Nn9bcQPV?;#T=_|MTC49jUWJvP??r7k-e%G_ zZ`&)Y=ZULM$sAlC|KRA!%-HbBOzQupS3Gb1nArKfC~wY|ZPPD4P5idr#oI~P+|%Hg ztlGS-96X;w{>}^ds$4Qh?&^H)%$kbgdyP}PUoG9Q@8o#;i*1?wLeq76#(NaP4!-WV zb!|t=7uJ`?xg}30P40d2_n`8kOrv-{nKq-p3okyEHBHg-5H%+Y(P-`ekV3Z8XKC;d(G^NhK zMeU67j%fW8iqA}V9yVJvFW&9--Se&ESs}J*A7&J8nRmfhp2vA()UBvx&wX|*uXY+5fCb*nO<_U}8@3oXxH`E*+P#GOV~0`t#2DcMo?RK62Jz z-i1Q7k9QZO9*T|fnffGbi)&?m&nNf&OF0eG1TGf4@13_s$>qO>{j?-6Ps6##iax+VWbkzLb6Mw<&;$VT$XqZ0 literal 0 HcmV?d00001 diff --git a/app/assets/images/icons/icon_options_active.png b/app/assets/images/icons/icon_options_active.png new file mode 100644 index 0000000000000000000000000000000000000000..5fff3956e454ff06f3280bac8c1b7eaa382da1a9 GIT binary patch literal 705 zcmeAS@N?(olHy`uVBq!ia0vp^svyk43?x4}TrdPu%*9TgAsieWw;%dH0CG15_=LFr z|NlRyPPnL51V~m-5UZIWR^BaI)+t&uS!}{`ss82CKyvaL*~x37XqqgEpP;S%1`Z^U1j!GWw51EJ_|%I108Q&666=mAZTpt0tBJEcb~Zn0`K0v z1A2%UP*ruq9%u|_fk$L90|U1Z2s2)~TlWVjC{yAZQ4*Y=R#Ki=l*-_klAn~S;FejG zTAp8&U98|7Z1!T$rXHX=5s*6PqSVBa%=|oskj&gv26KH=eM6H+pQki3FfdAZx;Tbd z^d6n^x~R!OpmpKa@SME`dm1%Zx2_0fj9s|zf3s=8sx2uKC6_<`IWcEazS(SFv#ui3 zl}4e7|B7D4e*RFYvFhi9!&i=1ZCSqPTj1WZLm|483_2gFe-)Xc_jyWHwv4l|2(#c; zy+t+^=^FJ?lbG8Nt&DeyG>~QJqOIc~fNPg>miLVQtt;gw~RpB$+44{AHt zwy*s( zYFtvL?Xk79Yi{1l&fU9U+q%2|{xPlkBP_E##y7s_d95A8y1R3LVaVX=>gTe~DWM4f D-Gp4! literal 0 HcmV?d00001 diff --git a/app/assets/images/icons/icon_options_disable.png b/app/assets/images/icons/icon_options_disable.png new file mode 100644 index 0000000000000000000000000000000000000000..4d13893afd35785a8d386b20587e7c79a938d0d7 GIT binary patch literal 627 zcmeAS@N?(olHy`uVBq!ia0vp^svyk43?x4}TrdPu%*9TgAsieWw;%dH0CHObd_r9R z|Nrmf;}Z}N03^f1!$U$sf`fx2A|fg(DvFAVIyyQU8X9VAYpbfNfFgN$c{w>b5O!Hv zS#fbOP)$KW0Z{zy+qW-YzI^`t`O~LQA3uKl@ZrPz_wV1md-v9@TQ_gsyng-q)vH%8 zU%q_e#EE0ajvYRH_~5~VyLRo$%F0ShOH1zYyA5=YPDzkoFoU46v5SjK=%^>g& z0)Xzs09z~@dV%^l3p^r=85p>QK$!8;-MT+OL75WQh?3y^w370~qErUQl>DSr1-Hzi z)bjkI>|zDyV6zu{HuV72iGb8O7o{eaWaj57gkKmFo`aGqPfq{|D)5S5w zqW9>OyG4f-1X`W5>yx6_=4L56F`d2`_50uZ>j{C260gQp>PfKevn(!aU#hWAZQ^Cs zGluGJ>t`>R@0`2ryRDB+PdK;bm87pGUNbbNOR8o3)d*U_e>9+KQ@`4gIgLx^d|mOq zKA7zp&r&H(-8EBstsE^UuqJToIs2BWCaTX}xT+$o{QJFMd21t2ZDHARcNyynQyx*9 zjNOYLoD8sv`a3hzOgt%<@4@Bk8!hHO;@1>yIymcsQN_{9%A$?2eKz;k=PD)Kubfx3 z)!JJ!C@8Q>^nB#2h|2ZBBLCmTU-Ibs)V;9E_zLgainYc*pZ}I!mcPBb=$~ak=+y1) j@ds4@vm6%(1hQ=fB*dX^Xu2IpFe;8`0?Yrckf=leEH&c5 zojG%6R#sM8S{n1m7#W~z%}Rp&f*Ayjja^(^LU*6J3jsi@-n|35057n3A#)UH0%w6o zWHAE+w-5+3Ub-L1;Fyx1l&avCS(I9yUzA;};2doBV$Y@? zpgIwdI_IL)#FEVXJcW?V+*Ag0eN%lylSiMYG%_$SN_o0ChFJ6-z4EfC$v}iP;nJ;E zy|TmJI-x9%OFaboYX664F9-=J*8Tf*W+UeZ-yNJP(@M8-1pk`htj>1+wpRS_n|86~ zGj{CcKUOlQ@Pk3d%ni)VD~l3?mD~G6J3jC~JvvE9MJ4%t z%c%uI44qDC0f(Q(HQZ-mdusB1*{{lvkuN?^34Lh9E~dAWTOq!!N2^r6^6n)m)x~LC z5m(oWE=Y@s6*_yv$YImf-)A4JTKH?utdLg4&Q+SumWfkzLT>qlui`qn_oC{r#LTKtah{7zKW(1$)R$Fi_v916P-O6Q^>bP0l+XkK=G9nG literal 0 HcmV?d00001 diff --git a/app/views/shared/_js_templates.html.erb b/app/views/shared/_js_templates.html.erb index 0eafd8ad8e..1b28d1b5a7 100644 --- a/app/views/shared/_js_templates.html.erb +++ b/app/views/shared/_js_templates.html.erb @@ -600,4 +600,9 @@
  • google+
  • - \ No newline at end of file + + + + diff --git a/lib/assets/javascripts/gfw/ui/umd_options.js.erb b/lib/assets/javascripts/gfw/ui/umd_options.js.erb new file mode 100644 index 0000000000..33ea6b2da0 --- /dev/null +++ b/lib/assets/javascripts/gfw/ui/umd_options.js.erb @@ -0,0 +1,274 @@ +gfw.ui.model.UmdOptions = cdb.core.Model.extend({ + defaults: { + tooltip: $('body').hasClass('home') ? "Share this map view" : "Share this graphic" + } +}); + +gfw.ui.view.UmdOptions = cdb.core.View.extend({ + className: 'umdoptions', + + events: { + 'click .umd-options-control' : '_openUMDoptions' + }, + + initialize: function() { + _.bindAll( this, '_toggle' ); + + this.model = new gfw.ui.model.Share(); + + this.model.on('change:hidden', this._toggle); + + var template = $('#umd-options-template').html(); + + this.template = new cdb.core.Template({ + template: template, + type: 'mustache' + }); + + this._initViews(); + }, + + _initViews: function() { + this.umdoptions = new gfw.ui.view.umdoptionsDialog({ + bitly: { + key: "<%= ENV['BITLY_API_KEY'] %>", + login: 'vizzuality' + } + }); + + $('body').append(this.umdoptions.render()); + }, + + _openUMDoptions: function(e) { + e.preventDefault(); + + this.umdoptions.show(); + }, + + _toggle: function() { + if (this.model.get('hidden')) { + this.$el.hide(); + } else { + this.$el.show(); + } + }, + + show: function() { + this.model.set('hidden', false); + }, + + hide: function() { + this.model.set('hidden', true); + }, + + render: function() { + var that = this; + + this.$el.append(this.template.render( this.model.toJSON() )); + this.$analysis_control = (this.$el.find('.analysis_control').length > 0) ? this.$el.find('.analysis_control') : null; + if (!!this.$analysis_control) { + $(this.$analysis_control).tipsy({ title: 'data-tip', fade: true, gravity: 'w' }); + } + + return this.$el; + } +}); + + +gfw.ui.model.umdoptionsDialog = Backbone.Model.extend({ + defaults: { + title: $('body').hasClass('home') ? "Share this map view" : "Share this graphic", + help: "Click and paste link in email or IM", + button_title: "Copy URL", + hidden: false, + mode: "link" + } + +}); + +gfw.ui.view.umdoptionsDialog = gfw.ui.view.Widget.extend({ + className: 'share_dialog', + + events: { + 'click .close': 'hide', + 'click .mode_menu a': '_onClickMode' + }, + + initialize: function() { + _.bindAll( this, 'toggle', '_toggleMode', '_onKeyDown' ); + + this.options = _.extend(this.options, this.defaults); + + this.model = new gfw.ui.model.umdoptionsDialog(); + + this.model.on('change:hidden', this.toggle); + this.model.on('change:mode', this._toggleMode); + + this.$backdrop = $('.backdrop'); + + var template = $('#share_dialog-template').html(); + + this.template = new cdb.core.Template({ + template: template, + type: 'mustache' + }); + }, + + show: function() { + this._setLink(); + this.$el.fadeIn(250); + this.$backdrop.show(); + + this._initBindings(); + }, + + hide: function(e) { + e && e.preventDefault(); + + var that = this; + + this.$el.fadeOut(250, function() { + that._setMode('link'); + }); + + this.$backdrop.fadeOut(250); + + this._disableBindings(); + }, + + _initBindings: function() { + var that = this; + + $(document).on('keydown', this._onKeyDown); + + this.$backdrop.on('click', function() { + that.hide(); + }); + + this.$input.on('click',function(){ + this.select() + }); + }, + + _disableBindings: function() { + $(document).off('keydown'); + + this.$backdrop.off('click'); + }, + + _setLink: function() { + var that = this; + + // Disable textarea + this.$input.val(''); + this.$loading.show(); + + // Get link short + this._generateShortUrl(window.location.href, function(url) { + that.$input.val(url); + + that.$share.find('.twitter').attr('href', "https://twitter.com/share?url=" + url); + that.$share.find('.facebook').attr('href', "https://www.facebook.com/sharer.php?u=" + url); + that.$share.find('.google_plus').attr('href', "https://plus.google.com/share?url=" + url); + + that.$loading.hide(); + }); + }, + + _setEmbed: function() { + var $body = $('body'), + dim_x = 640, + dim_y = 530; + + if (!$body.hasClass('home')) { + dim_x = 1000; + dim_y = ($body.hasClass('show')) ? 480 : 600; + } + + this.$input.val(''); + }, + + _generateShortUrl: function(url, callback) { + $.ajax({ + url:"https://api-ssl.bitly.com/v3/shorten?longUrl=" + encodeURIComponent(url) + "&login=" + this.options.bitly.login + "&apiKey=" + this.options.bitly.key, + type:"GET", + async: false, + dataType: 'jsonp', + success: function(r) { + if (!r.data.url) { + callback && callback(url); + + throw new Error('BITLY doesn\'t allow localhost alone as domain, use localhost.lan for example'); + } else { + callback && callback(r.data.url) + } + }, + error: function(e) { callback && callback(url) } + }); + }, + + _onKeyDown: function(e) { + if (e.which == 27) this._onEscKey(e); + }, + + _onEscKey: function(e) { + e && e.preventDefault(); + + this.hide(); + }, + + _onClickMode: function(e) { + e && e.preventDefault(); + + var $target = $(e.target), + mode = $target.attr('data-embed'); + + if (mode != this.model.get('mode')) { + $('.mode_menu li').removeClass('selected'); + $target.closest('li').addClass('selected'); + + this._setMode(mode); + } + }, + + _setMode: function(mode) { + this.model.set({ mode: mode }); + }, + + _toggleMode: function() { + var mode = this.model.get('mode'); + + if (mode === 'url') { + this.model.set({ + help: this.model.defaults.help, + button_title: this.model.defaults.button_title + }); + + this._setLink(); + } else if (mode === 'embed') { + this.model.set({ + help: 'Click and paste HTML to embed in website', + button_title: 'Copy code' + }); + + this._setEmbed(); + } + + this.$help.html(this.model.get('help')); + this.$button.html(this.model.get('button_title')); + }, + + render: function() { + var that = this; + + this.$el.append(this.template.render( this.model.toJSON() )); + + this.$help = this.$el.find('.help span'); + this.$input = this.$el.find('.field'); + this.$button = this.$el.find('.copy'); + this.$loading = this.$el.find('.umdoptions_loading'); + this.$share = this.$el.find('.share_buttons'); + + return this.$el; + } +}); From 95c0bd1680e923af6a27909073e0b82f322e9d75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A1n=20P=C3=A9rez?= Date: Wed, 7 May 2014 17:45:20 +0200 Subject: [PATCH 002/823] umd options layer: dialog template --- lib/assets/javascripts/gfw/ui/umd_options.js.erb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/assets/javascripts/gfw/ui/umd_options.js.erb b/lib/assets/javascripts/gfw/ui/umd_options.js.erb index 33ea6b2da0..be84fdbce7 100644 --- a/lib/assets/javascripts/gfw/ui/umd_options.js.erb +++ b/lib/assets/javascripts/gfw/ui/umd_options.js.erb @@ -87,7 +87,7 @@ gfw.ui.model.umdoptionsDialog = Backbone.Model.extend({ }); gfw.ui.view.umdoptionsDialog = gfw.ui.view.Widget.extend({ - className: 'share_dialog', + className: 'umdoptions_dialog', events: { 'click .close': 'hide', @@ -106,7 +106,7 @@ gfw.ui.view.umdoptionsDialog = gfw.ui.view.Widget.extend({ this.$backdrop = $('.backdrop'); - var template = $('#share_dialog-template').html(); + var template = $('#umdoptions_dialog-template').html(); this.template = new cdb.core.Template({ template: template, From b74dc2b7bcceeac0836212d64c587971948bb663 Mon Sep 17 00:00:00 2001 From: Pedro Garcia Date: Wed, 21 May 2014 10:33:30 +0200 Subject: [PATCH 003/823] first steps, refactoring page --- .../backgrounds/wall_background_alpha.png | Bin 0 -> 21288 bytes .../images/country-icons-s2672b16f85.png | Bin 4814 -> 0 bytes .../images/country-icons-sa8358b09d0.png | Bin 0 -> 7375 bytes .../images/country-icons/country-indepth.png | Bin 0 -> 2526 bytes app/assets/images/wall-background-alpha.png | Bin 0 -> 21288 bytes app/assets/stylesheets/countries.scss | 934 +++++------------- app/controllers/countries_controller.rb | 13 +- app/views/countries/show_redesign.html.erb | 353 +++++++ config/routes.rb | 3 + 9 files changed, 601 insertions(+), 702 deletions(-) create mode 100644 app/assets/images/backgrounds/wall_background_alpha.png delete mode 100644 app/assets/images/country-icons-s2672b16f85.png create mode 100644 app/assets/images/country-icons-sa8358b09d0.png create mode 100644 app/assets/images/country-icons/country-indepth.png create mode 100644 app/assets/images/wall-background-alpha.png create mode 100644 app/views/countries/show_redesign.html.erb diff --git a/app/assets/images/backgrounds/wall_background_alpha.png b/app/assets/images/backgrounds/wall_background_alpha.png new file mode 100644 index 0000000000000000000000000000000000000000..1ac0a5a102eb15807a6f4366a619f09a4fc44574 GIT binary patch literal 21288 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 literal 0 HcmV?d00001 diff --git a/app/assets/images/country-icons-s2672b16f85.png b/app/assets/images/country-icons-s2672b16f85.png deleted file mode 100644 index b55dc59d4940297238e6f802f56fcef57bbfe44d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4814 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 diff --git a/app/assets/images/country-icons-sa8358b09d0.png b/app/assets/images/country-icons-sa8358b09d0.png new file mode 100644 index 0000000000000000000000000000000000000000..d4ea65f1b7dbf98a63a9d775bfaaae41ff1cf4fb GIT binary patch literal 7375 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 IG@iWvAGHMV7XSbN literal 0 HcmV?d00001 diff --git a/app/assets/images/country-icons/country-indepth.png b/app/assets/images/country-icons/country-indepth.png new file mode 100644 index 0000000000000000000000000000000000000000..3da4ab5d2e4b4f3f5adaeda2b13efd6b461e3f36 GIT binary patch literal 2526 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 literal 0 HcmV?d00001 diff --git a/app/assets/images/wall-background-alpha.png b/app/assets/images/wall-background-alpha.png new file mode 100644 index 0000000000000000000000000000000000000000..1ac0a5a102eb15807a6f4366a619f09a4fc44574 GIT binary patch literal 21288 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 literal 0 HcmV?d00001 diff --git a/app/assets/stylesheets/countries.scss b/app/assets/stylesheets/countries.scss index 84a63cc568..74875c432c 100644 --- a/app/assets/stylesheets/countries.scss +++ b/app/assets/stylesheets/countries.scss @@ -1,16 +1,21 @@ -@import 'compass/css3/box-shadow'; -@import 'compass/css3/border-radius'; -@import 'compass/css3/opacity'; -@import 'compass/css3/background-size'; -@import 'compass/css3/images'; -@import 'compass/css3/text-shadow'; -@import 'compass/css3/inline-block'; - -@import 'fonts'; -@import 'helpers'; - -@import 'country-icons/*.png'; -/**/ +@import "compass/css3/box-shadow", + "compass/css3/box-sizing", + "compass/css3/border-radius", + "compass/css3/opacity", + "compass/css3/background-size", + "compass/css3/images", + "compass/css3/text-shadow", + "compass/css3/inline-block", + "fonts", + "helpers"; + +@import "country-icons/*.png"; + +$primary-color: #A0B941; +$default-color: #463F52; +$font-light: "fira_sans_otlight", Georgia, sans-serif; +$font-regular: "fira_sans_otregular", Georgia, sans-serif; +$font-medium: "fira_sans_otmedium", Georgia, sans-serif; .countries { &.index .content { @@ -185,773 +190,302 @@ /* =Show ----------------------------------------------- */ - -.country_graphs { - padding-top: 0; - - .columns { - padding: 14px 0 57px; - } - .share_control { - position: relative; - display: block; - width: 38px; - height: 38px; - left: 960px; - top: -38px; - @include country-icons-sprite(share_button); - - &:hover { @include country-icons-sprite(share_button_hover); } - &:active { @include country-icons-sprite(share_button_active); } - } +.country-show { + font-family: $font-medium; } -.graph { - height: 266px; - - &.map { - .frame, - .info { - background: #444; - } +.country-header { + width: 100%; + color: $default-color; + border-bottom: 1px solid #CCCCCC; - .action { - border-color: #444; - @include country-icons-sprite(country_action); - background-color: #fff; - } + .country-header-inner { + height: 350px; + background: #383643 image-url('backgrounds/wall_background_alpha.png'); + //@include background-size(cover); } - &.forest_loss { - .frame, - .info { - background: #FFC926; - } + .country-title { + padding: 20px; - .action { - border-color: #FFC926; - @include country-icons-sprite(forest_loss_action); - background-color: #fff; + h1 { + color: white; + font-size: 47px; + max-width: 500px; + margin-right: 20px; + display: inline-block; + font-weight: normal; } - .graph-title, - .graph-amount { - color: #FFC926; + .country-selector { + display: inline-block; + vertical-align: top; + margin-top: 2px; + border: 2px solid #73707D; + border-radius: 40px; + height: 39px; + width: 310px; } - } - .frame { - position: absolute; - top: 0; left: 0; - height: 100%; - width: 100%; - background: #FF4D4D; - @include border-radius(134px); - } - - &.ghost { - @include opacity(.5); + select { + border: none; + font-size: 13px; + text-transform: uppercase; + background: transparent; + color: #BEBCC2; + -webkit-appearance: none; + padding: 9px 15px; + width: 100%; - .action { - cursor: default; + &:focus { + outline: 0; + } } - } + } // country-title - .coming_soon { - display: none; - position: absolute; - z-index: 10; - top: 50%; left: 0; - width: 206px; - padding: 0 30px; - margin-top: -9px; - font-weight: bold; - font-size: 17px; - text-transform: uppercase; - text-align: center; - color: #111; - } + .country-details { + background-color:rgba(0, 0, 0, 0.2); + height: 261px; - .info { - position: absolute; - z-index: 10; - top: -14px; left: 50%; - border: 2px solid #fff; - margin-left: -14px; - width: 24px; - font-size: 14px; - line-height: 24px; - @extend .serif; - color: #fff; - text-align: center; - background: #FF4D4D; - @include border-radius(14px); - } + .country-preview { + width: 735px; + height: 100%; + padding: 30px; + float: left; + @include box-sizing(border-box); - .action { - position: absolute; - z-index: 10; - bottom: -20px; left: 50%; - border: 2px solid #FF4D4D; - margin-left: -20px; - width: 36px; - height: 36px; - @include country-icons-sprite(action); - background-color: #fff; - @include border-radius(20px); - - &.disabled { - cursor: default; - - &:after { - content: ''; - position: absolute; - top: -2px; left: -2px; - display: block; - width: 40px; - height: 40px; - background: #fff; - @include opacity(.5); + h4 { + text-transform: uppercase; + color: white; + line-height: 18px; + font-size: 14px; } - } - } - img , - .frame_bkg { - position: absolute; - top: 5px; left: 5px; - height: 256px; - width: 256px; - @include border-radius(129px); - } + .map, + .tree-numbers, + .loss-gain-graph { + float: left; + height: 100%; + } - .frame_bkg { - background: #fff; - } + .map { + width: 245px; + } - path { - stroke: #FF4D4D; - stroke-width: 4px; - fill: none; - } -} + .tree-numbers { + width: 140px; -.forma_marker { - stroke: #fff; - stroke-width: 2px; - fill: #FF4D4D; -} + h4 { + width: 100px; + } -.graph-title { - position: absolute; - z-index: 10; - top: 42px; left: 0; - width: 100%; - color: #FF4D4D; - font-size: 18px; - @extend .serif; - text-align: center; -} + .amount, + .unit { + color: $primary-color; + display: inline-block; + } -.graph-subtitle { - position: absolute; - z-index: 10; - top: 62px; left: 0; - width: 100%; - font-size: 13px; - @extend .serif; - text-align: center; - color: #666; - text-decoration: underline; + .amount { + font-size: 47px; + font-weight: bold; + } - span, - a { - line-height: 1.4; - background: #fff; - padding: 0 5px; - } + .tree-cover { + margin-bottom: 25px; + } - a { - color: #666; + .total-extension .amount { + font-size: 29px; + } + } - &:after { - content: ''; - position: absolute; - top: 50%; - margin: -1px 0 0 3px; - border-color: #666 rgba(#000, 0); - border-width: 4px 4px 0 4px; - border-style: solid; + .loss-gain-graph { + width: 250px; + } } - } -} -.graph-amount { - position: absolute; - z-index: 10; - bottom: 57px; left: 0; - width: 100%; - font-size: 27px; - @extend .serif; - color: #FF4D4D; - text-align: center; - - span { - display: inline-block; - line-height: .8; - padding: 0 5px; - background: #fff; - } -} - -.graph-action-subtitle, -.graph-date { - position: absolute; - z-index: 100; - font-size: 13px; - width: 100%; - @extend .serif; - text-align: center; - color: #333; -} - -.graph-date { - bottom: 37px; left: 0; -} - -.graph-action-subtitle { - bottom: -50px; left: 0; -} - -.hansen_dropdown-menu, -.forma_dropdown-menu { - display: none; -} - -.bar { - fill: #FFC926; - shape-rendering: "crispEdges"; - @include opacity(.5); - - &.last { - @include opacity(1); - } -} - -.bar-container { - position: relative; - height: 48px; -} - -.bar-inner { - position: absolute; - z-index: 10; - top: 0; -} - -.country_indepth { - height: 48px; - width: 100%; - background: #ccc; - - p { - font-size: 17px; - line-height: 48px; - @extend .serif; - text-align: center; - color: #555; - } - - a { - text-decoration: underline; - color: #333; - - &:hover { - color: darken(#333, 5%); + .country-sidenav { + float: right; + width: 215px; + height: 100%; + background: #F2F2F3; + + > ul { + padding: 10px 0; + width: 100%; + + > li { + height: 53px; + width: 100%; + padding: 20px; + @include box-sizing(border-box); + font-weight: bold; + text-transform: uppercase; + color: #464152; + font-size: 13px; + cursor: pointer; + + &:hover { + background: #FFFFFF; + } + } + } } - } - - strong { - font-weight: bold; - } -} -.country_menu { - height: 48px; - width: 100%; - background: #242424; - - li { - position: relative; - float: left; - width: 190px; - text-align: center; - border-left: 1px solid #4E4E4E; - - &:last-child { - border-right: 1px solid #4E4E4E; - } + } // country-details - &.disabled { - @include opacity(.5); - @include pointer-events(none); + .country-indepth { + clear: both; + border: 1px solid #CCCCCC; + border-bottom: 0; + height: 185px; + width: 100%; + padding: 35px 20px; + color: $default-color; + @include box-sizing(border-box); + + //@include country-icons-sprite(country-indepth); + + .country-indepth__title { + font-size: 29px; } - } - - a { - display: block; - color: #BEBEBE; - font-size: 17px; - line-height: 48px; - @extend .serif; - text-decoration: none; - &:hover { - color: #fff; + .country-indepth__body { + text-transform: uppercase; + font-size: 14px; + font-weight: 700; + margin-top: 20px; } - &.active { - color: #fff; + .country-indepth__links { + margin-top: 20px; - &:after { - content: ''; - position: absolute; - bottom: 0; left: 50%; - margin-left: -3px; - border: 6px solid transparent; - border-bottom-color: #f5f5f5; + a { + display: inline-block; + vertical-align: top; + color: $primary-color; + font-weight: 700; } } - } -} - -.section-title { - margin-bottom: 40px; - font-size: 45px; - line-height: 1.1; - @extend .serif; - color: #444; - - .info i { top: -12px; } - span { - color: $cGreen; - } -} + } // country-indepth -.section-subtitle { - margin-bottom: 10px; - font-size: 17px; - @extend .serif; - color: #888; - - .info { - float: left; - margin-left: -30px; - margin-top: -3px; - } -} - -.country_state { - background-position: 48px 0; - background-image: image-url('backgrounds/bkg_country.png'); - - .inner { - padding-left: 370px; - width: 600px; - } - - .country_alt { - line-height: 1.4; - } -} - -.country-path { - position: relative; - float: left; - margin-left: -360px; - height: 302px; - - svg { - position: absolute; - } -} - -.country_main { fill: #444; } -.country_alt { fill: #999; } - -.country_state-title { - padding-top: 80px; -} - -.forest_type, -.forest_tenure { - display: none; -} - -.forest_type.tall { - padding-top: 80px; -} - -.line-graph { - font-weight: bold; - text-transform: uppercase; - - circle { - stroke-width: 2px; - stroke: #f5f5f5; - } - - text { - @extend .sans-serif; - font-size: 11px; - } - - .one { - fill: #75B22E; - } - - .two { - fill: #AAC700; - } - - .three { - fill: #AC0; - } - - .four { - fill: #FFD24D; - } -} - -.country_alt { - margin-top: 30px; - font-style: italic; - @extend .serif; -} - -.man_list { - margin-bottom: 10px; - color: #ccc; -} - -section.country_people { - background-position: right 0; - background-image: image-url('backgrounds/bkg_country.png'); - - .gross_value.narrow { - float: left; - width: 600px; - } - - .employment { - float: left; - width: 460px; - - &.short { - width: 300px; - margin-left: 60px; - } - - .section-title { - @extend .clearfix; - margin-bottom: 10px; + .country-nav { + width: 100%; - div, - span { + > ul { + width: 100%; + text-align: center; + + > li { + @include box-sizing(border-box); + padding: 10px 15px; + height: 50px; + border: 1px solid #ccc; + width: 135px; float: left; - } - - span { - width: 250px; - margin: 10px 0 0 10px; + margin-right: -1px; font-size: 13px; - line-height: 1.4; - @extend .sans-serif; + font-weight: 700; + color: #464052; text-transform: uppercase; - color: #444; - } - } - } -} - -.country_indepth { - height: 48px; - width: 100%; - background: #ccc; - - p { - font-size: 17px; - line-height: 48px; - @extend .serif; - text-align: center; - color: #555; - } - - a { - color: #333; - text-decoration: underline; + cursor: pointer; + background: #F2F2F3; - &:hover { - color: darken(#333, 5%); - } - } - - strong { - font-weight: bold; - } -} - -section.country_laws { - background-position: 48px 0; - background-image: image-url('backgrounds/bkg_country.png'); - font-size: 15px; - line-height: 1.6; - - a { - color: #555; - - &:hover { - text-decoration: underline; - } + &:first-child { + width: 156px; + } - &.people-link:hover { - text-decoration: none; + &:hover { + color: $primary-color; + background: #F9F9F9; + } + } } - } -} + } // country-nav -.people-link { - display: inline-block; - border: 2px solid rgba(#ACCD00, .5); - line-height: 30px; - padding: 0 15px; - margin-top: 10px; - font-size: 15px; - text-decoration: none; - @include border-radius(18px); - color: #555; + display: table; +} // country-header - &:hover { - border: 2px solid #ACCD00; - color: #444; - } +.highlight { + color: $primary-color; } -section.country_climate { - padding: 0; +.country-section { + border-bottom: 1px solid #CCCCCC; + width: 100%; + padding: 40px 0; - .inner { - padding: 0; - margin: 0 auto; - } + .section-info { + margin-bottom: 10px; + font-size: 14px; + color: $default-color; + font-family: $font-medium; + text-transform: uppercase; + letter-spacing: -0.2px; - ul { - margin-right: -50px; + .info { + margin-right: 10px; + } } - li { - float: left; - width: 418px; - padding: 50px 35px 50px 0; + .section-content { + padding-left: 30px; + @extend .clearfix; - &.wide { - padding-right: 0; - width: 100%; + .left-col, + .right-col { + float: left; } - &.last { - background: #444; - text-align: right; - padding: 50px 50px 50px 35px; - - &.wide { - width: 870px; - } - - .info { - float: right; - margin-right: -30px; - margin-left: 0; - } + .left-col { + width: 40%; + } - .section-title { - color: #fff; - } + .right-col { + width: 55%; + padding-left: 5%; } .section-title { - margin: 0; + font-size: 30px; + line-height: 36px; + font-family: $font-medium; + color: $default-color; } } } -.section.country_conventions { - background-image: image-url('backgrounds/bkg_country.png'); - background-position: right 0; +.country-state { - .section-subtitle { margin-bottom: 20px; } +} // country-state - ul { - margin-right: -15px; +.country-employment { + .section-content .left-col { + width: 460px; } - li { - display: block; - position: relative; - float: left; - margin: 0 15px 15px 0; - padding-top: 25px; - width: 120px; - height: 67px; - border: 2px solid #aaa; - font-weight: bold; - font-size: 12px; - @extend .sans-serif; - text-align: center; - color: #999; - @include border-radius(2px); + .section-title { + @extend .clearfix; + margin-bottom: 10px; + div, span { - position: absolute; - display: block; - left: 0; right: 0; bottom: 0; - padding: 5px; - border-top: 1px solid #ccc; - font-weight: normal; - font-style: italic; - line-height: 12px; - text-transform: uppercase; - } - } -} - -.country_external_links { - font-size: 13px; - color: #ddd; - margin-bottom: -10px; - - li { - @include inline-block(); - border-left: 1px solid #ddd; - padding-left: 8px; - margin: 0 0 10px 5px; - line-height: 14px; - - &:first-child { - border: 0; - margin-left: 0; - padding-left: 0; + float: left; } - } - a { - text-decoration: underline; - } -} - -.country_download_links { - li { - @include inline-block(); - margin-left: 7px; - - a, span { - @include inline-block(); - border: 2px solid rgba($cGreen, .5); - padding: 0 20px; - font-weight: bold; + width: 250px; + margin: 10px 0 0 10px; font-size: 13px; - line-height: 44px; + line-height: 1.4; + @extend .sans-serif; text-transform: uppercase; - color: #333; - @include border-radius(25px); + color: #444; } - - a:hover { - border: 2px solid $cGreen; - } - - span { @include opacity(.4); } } } -.country_blog { - text-align: center; - - .columns { - padding: 0; - min-height: 340px; - } - - .column-title { - position: relative; - top: -40px; - font-weight: bold; - font-size: 13px; - text-transform: uppercase; - color: #555; - } - - .story-title { - margin-bottom: 20px; - font-size: 21px; - @extend .serif; - color: #FFF; - } - - .story-content { - margin-bottom: 30px; - color: #666; - - a { - font-weight: normal; - text-decoration: underline; - color: #666; - } - } - - .column { - display: block; - margin-top: 40px; - min-height: 266px; - - &.first, - &.last { - margin-top: 52px; - - .column-title { - top: -52px; - } - } - - &.first { - text-align: left; - } - - &.last { - text-align: right; - } - - &.no_story { - .frame { - background: #111; - } - - .gradient { - background-image: image-url('backgrounds/bkg_circles_gradient_nostory.png'); - } - } - - .inline { - display: inline; - width: auto; - height: auto; - } - } -} - - /* =Overview ----------------------------------------------- */ diff --git a/app/controllers/countries_controller.rb b/app/controllers/countries_controller.rb index 30fb1d6727..3d9b3277f8 100644 --- a/app/controllers/countries_controller.rb +++ b/app/controllers/countries_controller.rb @@ -1,7 +1,7 @@ class CountriesController < ApplicationController - before_filter :load_countries, :only => [:index] + before_filter :load_countries, :only => [:index, :show_redesign] + include ActionView::Helpers::NumberHelper - # GET /country/:id def show country = Api::Country.find_by_iso(params[:id])['countries'][0] @@ -16,6 +16,15 @@ def show @country = country end + def show_redesign + @country = Api::Country.find_by_iso(params[:id])['countries'][0] + not_found unless @country.present? + + @country['gva_percent'] = (@country['gva_percent'] < 0.1) ? number_to_percentage(@country['gva_percent'], precision: 2) : number_to_percentage(@country['gva_percent'], precision: 1) + @employees = @country['employment'] + @conventions = %w(cbd unfccc kyoto unccd itta cites ramsar world_heritage nlbi ilo) + end + private def load_countries diff --git a/app/views/countries/show_redesign.html.erb b/app/views/countries/show_redesign.html.erb new file mode 100644 index 0000000000..0a98b292b4 --- /dev/null +++ b/app/views/countries/show_redesign.html.erb @@ -0,0 +1,353 @@ +

    +
    +
    + + +
    +
    +

    <%= @country['name'] %>

    +
    + +
    +
    + +
    +
    +
    +
    +
    +

    Tree cover

    + + <%= number_to_human(@country['extent'])[0,3] %> + mha +
    +
    +

    Total extension

    + 851 + mha +
    +
    +
    +

    LOSS AND GAIN (2001 - 2012)

    +
    +
    +
    +
      +
    • Info
    • +
    • Download data
    • +
    • Analyze
    • +
    +
    +
    +
    + + + <% if !@country['indepth'].present? %> +
    +

    Global Forest Watch does in-depth work in this country.

    +

    Find more information at the Atlas Foreiester Interactif de la République Democratique Du Congo

    + +
    + <% end %> + + +
    +
      +
    • Forma clearing alerts
    • +
    • Forest type
    • +
    • State of the forest
    • +
    • People & Economy
    • +
    • Tenure & Laws
    • +
    • Climate Change
    • +
    • International agreements
    • +
    +
    + +
    +
    + + +
    +
    + + +
    +
    +

    There were 7,363 FORMA alerts in october

    +
    +
    + +
    +
    +
    +
    + + +
    +
    + + +
    +
    +

    + <%= @country['name'] %> has <%= number_to_human(@country['extent'], precision: 2) %> hectares of tree cover +

    +
    +
    + +
    +
    +
    +
    + + +
    +
    + + +
    +

    + The forestry sector contributed USD <%= gva_to_human(@country['gva'])%> to the economy in 2006, which is appoximately <%= @country['gva_percent'] %> of the GDP. +

    +
    +
    +
    + + +
    +
    + + +
    +
    + <% if @employees.present? && @employees > 0 %> +
      '> +
    • + <% if @employees < 1000 %> +

      <%= @employees %>
      thousand people are directly employed by the forestry sector, according to 2006 FAO data

      + <% else %> +

      <%= (@employees/1000.00).round(2) %>
      million people are directly employed by the forestry sector, according to 2006 FAO data

      + <% end %> + + <% @employees = @employees < 100 ? @employees : 100 %> +
      + <% @employees.times do |i| %> + <%= image_tag 'countries/man.png' %> + <% end %> + + <%= '...' if @employees == 100 %> +
      +
    • +
    + <% end %> +
    +
    +
    +
    + + +
    +
    + + +
    +

    +

    +
    +
    +
    + + +
    +
    + + + +
    +
    + + +
    +
    + + +
    +
    +

    This country has <%= number_with_delimiter(@country['carbon_stocks']) %> million
    metric tons of carbon stocks
    in living forest biomass.

    +
    +
    + +
    +
    +
    +
    + + +
    +
    + + +
    +
      + <% @conventions.each do |convention| %> + <% if @country["convention_#{convention}"].present? %> +
    • <%= t('.conventions.'+convention+'_title_html') %> + <%= @country["convention_#{convention}"] %> +
    • + <% end %> + <% end %> +
    +
    +
    +
    + + + <% if @country['ministry_link'].present? || @country['external_links'].present? %> + + <% end %> + + + + + +
    +
    +

    <%= t('.country_blog.title', :country => @country['name'].titleize) %>

    + + +
    +
    + + +
    +
    +
    + + +
    +
    + +
    + <%= render 'shared/countries' %> + <%= render 'shared/sources' %> +
    + +
    diff --git a/config/routes.rb b/config/routes.rb index 61d2f67102..a0f4de6a05 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -26,6 +26,9 @@ get '/countries' => 'countries#index' get '/country/:id' => 'countries#show', :as => 'country' get '/countries/overview' => 'countries#overview' + # New country page + get '/r/country/:id' => 'countries#show_redesign', :as =>'country_redesign' + # media post 'media/upload' => 'media#upload' From d96dde2c87608ae03084ef154a4b3305cd5d3f7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A1n=20P=C3=A9rez?= Date: Wed, 21 May 2014 16:59:02 +0200 Subject: [PATCH 004/823] analysis: let analyse protected areas --- lib/assets/javascripts/gfw/gfw_lib.js.erb | 4 ++-- lib/assets/javascripts/gfw/ui/analysis.js.erb | 8 +++++++- lib/assets/javascripts/gfw/ui/protected-infowindow.js | 4 ++-- lib/assets/stylesheets/map.scss | 1 - 4 files changed, 11 insertions(+), 6 deletions(-) diff --git a/lib/assets/javascripts/gfw/gfw_lib.js.erb b/lib/assets/javascripts/gfw/gfw_lib.js.erb index 06da1b0267..c338cf26da 100644 --- a/lib/assets/javascripts/gfw/gfw_lib.js.erb +++ b/lib/assets/javascripts/gfw/gfw_lib.js.erb @@ -1096,7 +1096,7 @@ GFW.modules.app = function(gfw) { tiler_domain: 'dyynnn89u7nkm.cloudfront.net', sql_domain: 'dyynnn89u7nkm.cloudfront.net', tiler_path: '/tiles/', - extra_params: { v: this.global_version, cache_policy: "persist" }, // define a verison number on requests + extra_params: { v: this.global_version, cache_policy: "persist" }, // define a version number on requests tiler_suffix: '.png', table_name: this._getTableName(this.currentBaseLayer), query: 'SELECT * FROM global_7d', @@ -1535,7 +1535,7 @@ GFW.modules.maplayer = function(gfw) { Filter.toggle(id); } - if (slug === ('fires') || GFW.app.currentBaseLayer === 'fires' || GFW.app.currentBaseLayer === null) { + if (GFW.app.currentBaseLayer === null) { Analysis.hide(); } else { Analysis.show(); diff --git a/lib/assets/javascripts/gfw/ui/analysis.js.erb b/lib/assets/javascripts/gfw/ui/analysis.js.erb index c9e9ae0825..201a23b7d1 100644 --- a/lib/assets/javascripts/gfw/ui/analysis.js.erb +++ b/lib/assets/javascripts/gfw/ui/analysis.js.erb @@ -356,7 +356,7 @@ gfw.ui.view.Analysis = cdb.core.View.extend({ baselayer = config.BASELAYER, baseurl = "//<%= ENV['GFW_API_HOST'] %>/datasets/", range; - if (config.BASELAYER === 'fires' || config.BASELAYER === null) return; + if (config.BASELAYER === null) return; if (!this.initStats) { this.$el.find('.stats .title, .stats ul').hide(); @@ -631,6 +631,8 @@ gfw.ui.view.Analysis = cdb.core.View.extend({ // https://github.com/maxogden/geojson-js-utils var area = 0; var points = (typeof(polygon.features) !== 'undefined') ? polygon.features[0].geometry.coordinates[0] : polygon.coordinates[0]; + points = (points.length === 1 && points[0].length > 1) ? points[0] : points; + var j = points.length - 1; var p1, p2; @@ -690,6 +692,10 @@ gfw.ui.view.Analysis = cdb.core.View.extend({ } }, + _loadProtectedArea: function(the_geom,area_id, area_name){ + this.loadPolygon(the_geom) + }, + render: function() { var that = this; diff --git a/lib/assets/javascripts/gfw/ui/protected-infowindow.js b/lib/assets/javascripts/gfw/ui/protected-infowindow.js index 3a5abe7bc5..9f191e0c4b 100644 --- a/lib/assets/javascripts/gfw/ui/protected-infowindow.js +++ b/lib/assets/javascripts/gfw/ui/protected-infowindow.js @@ -38,7 +38,7 @@ ProtectedInfowindow.prototype.draw = function() { div.innerHTML = ''+ '
    '+ '
    ' + - '

    '+ + '

    '+ '
    ' + '
    '+ '
    ' + @@ -52,7 +52,7 @@ ProtectedInfowindow.prototype.draw = function() { google.maps.event.addDomListener(analyse, 'click', function (ev) { ev.preventDefault ? ev.preventDefault() : ev.returnValue = false; - Analysis._loadProtectedArea(me.content.id, me.content.name) + Analysis._loadProtectedArea(me.content.geom.the_geom, me.content.id, me.content.name) me._hide(); }); diff --git a/lib/assets/stylesheets/map.scss b/lib/assets/stylesheets/map.scss index d3ea9203c3..fbd29ed7e6 100644 --- a/lib/assets/stylesheets/map.scss +++ b/lib/assets/stylesheets/map.scss @@ -111,7 +111,6 @@ text-align: left; .analyse { - display: none; @include position(65px, 5px, false, false); @include size(41px, 42px); @include mixins-sprite(graph); From f040335fa6650c6caf4c1edd2aee3fe6ed2d6864 Mon Sep 17 00:00:00 2001 From: Pedro Garcia Date: Wed, 21 May 2014 18:18:37 +0200 Subject: [PATCH 005/823] donut d3, more css and html --- .../images/country-icons-s188448b620.png | Bin 0 -> 8345 bytes .../images/country-icons-sa8358b09d0.png | Bin 7375 -> 0 bytes .../images/country-icons/chart_inverse.png | Bin 0 -> 461 bytes .../images/country-icons/download_inverse.png | Bin 0 -> 282 bytes .../images/country-icons/info_inverse.png | Bin 0 -> 563 bytes app/assets/javascripts/countries.js | 614 ++++-------------- app/assets/javascripts/stories.js | 1 + app/assets/stylesheets/countries.scss | 189 +++++- app/views/countries/show_redesign.html.erb | 89 ++- 9 files changed, 347 insertions(+), 546 deletions(-) create mode 100644 app/assets/images/country-icons-s188448b620.png delete mode 100644 app/assets/images/country-icons-sa8358b09d0.png create mode 100644 app/assets/images/country-icons/chart_inverse.png create mode 100644 app/assets/images/country-icons/download_inverse.png create mode 100644 app/assets/images/country-icons/info_inverse.png diff --git a/app/assets/images/country-icons-s188448b620.png b/app/assets/images/country-icons-s188448b620.png new file mode 100644 index 0000000000000000000000000000000000000000..0d7fc003d48f13d006d7b6ae1b6e85eef723a613 GIT binary patch literal 8345 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 literal 0 HcmV?d00001 diff --git a/app/assets/images/country-icons-sa8358b09d0.png b/app/assets/images/country-icons-sa8358b09d0.png deleted file mode 100644 index d4ea65f1b7dbf98a63a9d775bfaaae41ff1cf4fb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7375 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 IG@iWvAGHMV7XSbN diff --git a/app/assets/images/country-icons/chart_inverse.png b/app/assets/images/country-icons/chart_inverse.png new file mode 100644 index 0000000000000000000000000000000000000000..35ac406cdffb88235e589409c69724e8cd37ed7b GIT binary patch literal 461 zcmV;;0W$uHP)WdJfUFEuYQ zG<-Bwu>b%79CSrkbW?9;ba!ELWdK2BZ(?O2Mrm?ocW-iQb09-gHt4*vi~s-uCrLy> zR49>S{Qv(y1L=T?fq_9^&w?Cn#DMeXFFtzogfwlB9z9vMY*k5dB?;Po{`}S0*k*2K zqOY&p+S2v!-#-Qh1{O?p&!4~e@ZqDTraA)y!;c?7Iy(9^HPrnBd{|jo6B9DNfB(VF z&BgHl|9?GQ^ZzjL{{4Fgdym4x^4G85{Qv)d!TcqamG%EI0E%sI-n?yZ>nSSEef;?8 zish@-HPo(MyHQ$JfW_&^w$;_OOqnwC|NsB5U%$!8DK0Q0TN&?cJd72;NZCZ(EkCD%UR$NS>_3HhZyWQx8yGiKmNWh{V+7KmY&RGlM|$#zs?So;iC9 zL=Gh+9pGl$X!3Xo=Vh@o@lBz6Dkcq;x4-G`ulu*F;Gxj67}rovVPm_EGB+i<+Z|cN-q0!#g_t*bdXyVB1;7Zx~|Nnnush80Y3oRMW9`SB+_p00003b3#c}2nYz< z;ZNWI000SaNLh0L01FZT01FZU(%pXi0000LbVXQnLvm$dbZKvHAXI5>WdJfUFEuYQ zG<-Bwu>b%79CSrkbW?9;ba!ELWdK2BZ(?O2Mrm?ocW-iQb09-gHt4*vi~s-ujY&j7 zR49>S{Qv(y0|NsC1H<3He>be(ykp0n>(_34`t+HPk5^Mm!^6WRAkdeYnHi#n5u*C; z-TQ_4A6Or;RO!^6FJ>0D)HMW(-h{}$$# zLzJ?xuw-VX?B2Wi(v_2om(Ed8kcX)L^5tt`e)-?Of0^}k4L5Gs0ulA|_bMwd{PgkD z{reB~^>vk1ls0YL3X%Ho;iH6v7|V_wd*C*1Sij}vi&v*lpZoLY&&ksVBqYR<-LqrI z9;S;IE+Y#aKYr@pzkhZ0m7JWMM-CrDmb!T1GSiPAKhXS?o0kz18gO9$;hye^$l^bK z{9xkX;6PLD>gx3K=daR|sy~1JAd7QwaIokb=$<@r8d)?iw}gqA`RC7HXr|~J=rVbD zxS)v&3kwMf@H1cl4-Xf{KY#uN`bQ$i;-)RD7#JA*e8P~GDk#XW-?*HKnVGqNVkZv| zH(cV-!J~%`9z|Bl!^7P_v6GpZne=o55hW%K0stVYz+Db|iAVqd002ovPDHLkV1nSB B0OtSz literal 0 HcmV?d00001 diff --git a/app/assets/javascripts/countries.js b/app/assets/javascripts/countries.js index dbc6658fd8..b29ef09e95 100644 --- a/app/assets/javascripts/countries.js +++ b/app/assets/javascripts/countries.js @@ -7,174 +7,59 @@ //= require gfw/ui/sourcewindow //= require gfw/ui/share - gfw.ui.view.CountriesShow = cdb.core.View.extend({ el: document.body, events: { - 'click .info': '_openSource', - 'click .forma_dropdown-link': '_openDropdown', - 'click .hansen_dropdown-link': '_openDropdown', - 'click .hansen_dropdown-menu a': '_redrawCircle' }, initialize: function() { _.bindAll(this, '_positionScroll'); this.iso = this.options.iso; + this.$nav = this.$('.country-nav'); + this.$indepth = this.$('.country-indepth'); - this.$nav = this.$('.country_menu'); - this.$indepth = this.$('.country_indepth'); - - this._stickynav(); + //this._stickynav(); this._initViews(); - this._initHansenDropdown(); }, - _initViews: function() { - this.sourceWindow = new gfw.ui.view.SourceWindow(); - this.$el.append(this.sourceWindow.render()); - - if (this.iso === 'CHN') { - var that = this; - - this._drawCountry(this.iso, function() { - that._drawCountry('TWN'); - }); - } else { - this._drawCountry(this.iso); - } - - this._drawForest(this.iso); + _initViews: function() { this._drawTenure(this.iso); - - this._drawCircle('forma', 'lines', { iso: this.iso }); - this._drawCircle('forest_loss', 'bars', { iso: this.iso, dataset: 'loss' }); - - Share = new gfw.ui.view.Share(); - this.$el.find('.country_graphs .inner').append(Share.render()); - }, - - _initFormaDropdown: function() { - $('.forma_dropdown-link').qtip({ - show: 'click', - hide: { - event: 'click unfocus' - }, - content: { - text: $('.forma_dropdown-menu') - }, - position: { - my: 'bottom right', - at: 'top right', - target: $('.forma_dropdown-link'), - adjust: { - x: -10 - } - }, - style: { - tip: { - corner: 'bottom right', - mimic: 'bottom center', - border: 1, - width: 10, - height: 6 - } - } - }); - }, - - _initHansenDropdown: function() { - this.dropdown = $('.hansen_dropdown-link').qtip({ - show: 'click', - hide: { - event: 'click unfocus' - }, - content: { - text: $('.hansen_dropdown-menu') - }, - position: { - my: 'top right', - at: 'bottom right', - target: $('.hansen_dropdown-link'), - adjust: { - x: 10 - } - }, - style: { - tip: { - corner: 'top right', - mimic: 'top center', - border: 1, - width: 10, - height: 6 - } - } - }); - }, - - _openSource: function(e) { - e.preventDefault(); - - var source = $(e.target).closest('.info').attr('data-source'); - - ga('send', 'event', 'SourceWindow', 'Open', source); - this.sourceWindow.show(source).addScroll(); - }, - - _openDropdown: function(e) { - e.preventDefault(); - }, - - _redrawCircle: function(e) { - e.preventDefault(); - - var dataset = $(e.target).attr('data-slug'), - subtitle = $(e.target).text(); - - var api = this.dropdown.qtip('api'); - - api.hide(); - - $('.hansen_dropdown-link').html(subtitle); - - if (dataset === 'countries_gain') { - this._drawCircle('forest_loss', 'comp', { iso: this.iso }); - } else { - this._drawCircle('forest_loss', 'bars', { iso: this.iso, dataset: dataset }); - } + this._drawForestsType(this.iso); + this._drawFormaAlerts(this.iso); }, _positionScroll: function() { this.indepth_bar = 0; - var h_min = $('.country_state').offset().top - 48, - h_max = $('.country_conventions').offset().top - 48; - - if (this.$('.country_indepth').length > 0) { - this.indepth_bar = 48; - - h_min -= 48; - h_max -= 48; - - if ($(window).scrollTop() > (h_min) && $(window).scrollTop() < h_max) { - this.$indepth.css({ - position: 'fixed', - top: 0 - }); - } else if ($(window).scrollTop() >= h_max) { - this.$indepth.css({ - position: 'absolute', - top: h_max - h_min - }); - } else { - this.$indepth.css({ - position: 'absolute', - top: 0 - }); - } - } + var h_min = $('.country-alerts').offset().top - 48, + h_max = $('.country-conventions').offset().top - 48; + + // if (this.$('.country-indepth').length > 0) { + // this.indepth_bar = 48; + + // h_min -= 48; + // h_max -= 48; + + // if ($(window).scrollTop() > (h_min) && $(window).scrollTop() < h_max) { + // this.$indepth.css({ + // position: 'fixed', + // top: 0 + // }); + // } else if ($(window).scrollTop() >= h_max) { + // this.$indepth.css({ + // position: 'absolute', + // top: h_max - h_min + // }); + // } else { + // this.$indepth.css({ + // position: 'absolute', + // top: 0 + // }); + // } + // } if ($(window).scrollTop() > (h_min) && $(window).scrollTop() < h_max) { this.$nav.css({ @@ -210,146 +95,6 @@ gfw.ui.view.CountriesShow = cdb.core.View.extend({ $(window).scroll(this._positionScroll); }, - _drawCountry: function(iso, callback) { - var that = this; - - var sql = ['SELECT the_geom', - 'FROM forest_cov_glob_v3', - "WHERE country_code = '"+iso+"'", - 'UNION', - 'SELECT the_geom', - 'FROM ne_50m_admin_0_countries', - "WHERE adm0_a3 = '"+iso+"'&format=topojson"].join(' '); - - d3.json('https://wri-01.cartodb.com/api/v2/sql?q='+sql, function(error, topology) { - if (iso === 'TWN') { - draw(topology, 0, 'CHN', { alerts: true, bounds: that.bounds }); - } else { - var bounds = draw(topology, 0, iso, { alerts: true }); - - if (iso === 'CHN') that.bounds = bounds; - } - - callback && callback(); - }); - }, - - _drawForest: function(iso) { - var sql = ["SELECT unnest(array['forest_primary', 'forest_regenerated', 'forest_planted'])", - 'AS type, unnest(array[COALESCE(forest_primary, 0),', - 'COALESCE(forest_regenerated, 0),', - 'COALESCE(forest_planted, 0)])', - 'AS percent', - 'FROM gfw2_countries', - "WHERE iso = '"+iso+"'"].join(' '); - - d3.json('https://wri-01.cartodb.com/api/v2/sql?q='+sql, function(json) { - var data = json.rows; - - _.each(data, function(type) { - if (type['percent'] !== 0) { - $('.country_state-title').css({ - 'padding-top': '40px' - }); - $('.forest_type').show(); - } - }); - - var svg = d3.select('.country_state .line-graph') - .append('svg') - .attr('width', 635) - .attr('height', 50); - - var x_extent = [0, 100], - x_scale = d3.scale.linear() - .range([0, 590]) - .domain(x_extent); - - var origins = [], - aggr = 0, - klass = ['one', 'three', 'four'], - type = ['Primary', 'Regenerated', 'Planted']; - - _.each(data, function(d, i) { - var current = data[i-1] && data[i-1]['percent'] || 0; - - aggr += current; - - origins[i] = aggr; - }); - - // add lines - svg.selectAll('rect') - .data(data) - .enter() - .append('rect') - .attr('class', function(d, i) { - return 'line ' + klass[i]; - }) - .attr('x', function(d, i) { - return x_scale(origins[i]); - }) - .attr('y', 19) - .attr('width', function(d) { - return x_scale(d['percent']); - }) - .attr('height', 4) - .attr('rx', 2) - .attr('ry', 2); - - // add balls - svg.selectAll('circle') - .data(data) - .enter() - .append('svg:circle') - .attr('class', function(d, i) { - return klass[i]; - }) - .attr('cx', function(d, i) { - return x_scale(d['percent']+origins[i]); - }) - .attr('cy', 21) - .attr('r', 5) - .style('visibility', function(d) { - return d['percent'] === 0 ? 'hidden' : 'visible'; - }); - - // add values - svg.selectAll('.text') - .data(data) - .enter() - .append('text') - .text(function(d, i) { - return type[i]+' '+d['percent']+'%'; - }) - .attr('class', function(d, i) { - return 'text ' + klass[i]; - }) - .attr('x', function(d, i) { - var w_line = $('.line.'+klass[i]).attr('width'), - w_text = $(this).width(); - - if ((i === 0 && w_text > w_line) || - (i === 1 && w_text > (parseFloat(w_line) + - parseFloat($('.line.one').attr('width'))))) { - return x_scale(d['percent']+origins[i]) - w_line; - } else if (i === 2 && w_text > w_line) { - return x_scale(d['percent']+origins[i]) - w_text; - } else { - var offset = (w_text > w_line) ? w_text : (w_line/2 + w_text/2); - - return x_scale(d['percent']+origins[i]) - offset; - } - }) - .attr('y', function(d, i) { - return (i % 2 === 0) ? 42 : 08; - }) - .style('visibility', function(d) { - return d['percent'] === 0 ? 'hidden' : 'visible'; - }); - }); - }, - _drawTenure: function(iso) { var sql = ['SELECT tenure_government, tenure_owned, tenure_owned_individuals,', 'tenure_reserved, GREATEST(tenure_government, tenure_owned,', @@ -394,8 +139,8 @@ gfw.ui.view.CountriesShow = cdb.core.View.extend({ _.each(tenures, function(tenure, i) { if (tenure['percent'] !== null && tenure['percent'] !== 0) { - if ($('.forest_tenure').not(':visible')) { - $('.forest_tenure').show(); + if ($('.country-tenure').not(':visible')) { + $('.country-tenure').show(); } h += 50; @@ -407,9 +152,9 @@ gfw.ui.view.CountriesShow = cdb.core.View.extend({ } }); - var svg = d3.select('.country_laws .line-graph') + var svg = d3.select('.country-tenure .line-graph') .append('svg') - .attr('width', 570) + .attr('width', 600) .attr('height', h); // add lines @@ -485,70 +230,103 @@ gfw.ui.view.CountriesShow = cdb.core.View.extend({ }); }, - _drawCircle: function(id, type, options) { - var that = this; + _drawForestsType: function(iso) { + var sql = ["SELECT unnest(array['forest_regenerated', 'forest_primary', 'forest_planted'])", + 'AS type, unnest(array[COALESCE(forest_regenerated, 0),', + 'COALESCE(forest_primary, 0),', + 'COALESCE(forest_planted, 0)])', + 'AS percent', + 'FROM gfw2_countries', + "WHERE iso = '"+iso+"'"].join(' '); - var $graph = $('.'+id), - $amount = $('.'+id+' .graph-amount'), - $date = $('.'+id+' .graph-date'), - $coming_soon = $('.'+id+' .coming_soon'), - $action = $('.'+id+' .action'); + d3.json('https://wri-01.cartodb.com/api/v2/sql?q='+sql, function(json) { + // TODO => if percents are 0 + var data = _.pluck(json.rows, 'percent'); + + var width = 225, + height = 225, + radius = Math.min(width, height) / 2, + colors = ['#819515', '#A1BA42', '#DDDDDD'], + labelColors = ['white', 'white', '#555']; + + var pie = d3.layout.pie() + .sort(null); + + var arc = d3.svg.arc() // create elements for using arc data + .innerRadius(radius - 67) + .outerRadius(radius) + + var svg = d3.select(".forests-type-graph") + .append("svg") + .attr("width", width) + .attr("height", height) + .append("g") + .attr("transform", "translate(" + width / 2 + "," + height / 2 + ")"); + + var path = svg.selectAll("path") + .data(pie(data)); + + path.enter().append("path") + .attr("fill", function(d, i) { return colors[i]; }) + .attr("d", arc); + + path.enter().append('text') + .attr('transform', function(d) { var c = arc.centroid(d); return 'translate(' + (c[0]-12) + ',' + (c[1]+8) + ')'}) + .text(function(d) { return d.data + '%' }) + .attr('fill', function(d, i) { return labelColors[i]; } ) + .style('font-size', '13px'); + }); + }, - $('.graph.'+id+' .frame_bkg').empty(); - $graph.addClass('ghost'); - $amount.html(''); - $date.html(''); - $coming_soon.hide(); + _drawFormaAlerts: function(iso) { + var that = this; - var width = options.width || 256, - height = options.height || width, - h = 100, // maxHeight - radius = width / 2; + var $graph = $('.forma-graph'); - var graph = d3.select('.graph.'+id+' .frame_bkg') + var width = 500, + height = 156, + h = 156, // maxHeight + radius = width / 2, + gridLinesCount = 7; + + // Add dashed grid + var graph = d3.select('.forma-graph') .append('svg:svg') - .attr('class', type) + .attr('class', 'line') .attr('width', width) .attr('height', height); - var dashedLines = [ - { x1:17, y:height/4, x2:239, color: '#ccc' }, - { x1:0, y:height/2, x2:width, color: '#ccc' }, - { x1:17, y:3*height/4, x2:239, color: '#ccc' } - ]; - - // Adds the dotted lines - _.each(dashedLines, function(line) { + var gridLineY = height; + for (var i = 0; i < gridLinesCount; i++) { graph.append('svg:line') - .attr('x1', line.x1) - .attr('y1', line.y) - .attr('x2', line.x2) - .attr('y2', line.y) - .style('stroke-dasharray', '2,2') - .style('stroke', line.color); - }); + .attr('x1', 0) + .attr('y1', gridLineY) + .attr('x2', width) + .attr('y2', gridLineY) + .style('stroke-dasharray', ('2, 3')) + .style('stroke', function() { return (i == 0) ? '#333' : '#CCC'; } ); + + gridLineY -= height/(gridLinesCount-1); + }; + // Render forma graph var sql = ["SELECT date_trunc('month', date) as date, COUNT(*) as alerts", 'FROM forma_api', - "WHERE iso = '"+options.iso+"'", + "WHERE iso = '"+iso+"'", "GROUP BY date_trunc('month', date)", "ORDER BY date_trunc('month', date) ASC"].join(' '); - if (type === 'lines') { - d3.json('https://wri-01.cartodb.com/api/v2/sql?q='+sql, function(json) { - if (json && json.rows.length > 0) { - $graph.removeClass('ghost'); - $action.removeClass('disabled'); - that._initFormaDropdown(); - - var data = json.rows.slice(1, json.rows.length); - } else { - $coming_soon.show(); - - return; - } + d3.json('https://wri-01.cartodb.com/api/v2/sql?q='+sql, function(json) { + if (json && json.rows.length > 0) { + var data = json.rows.slice(1, json.rows.length); + console.log(data); + } else { + console.log('no data'); + //$coming_soon.show(); + return; + }; - var x_scale = d3.scale.linear() + var x_scale = d3.scale.linear() .domain([0, data.length - 1]) .range([0, width - 80]); @@ -570,12 +348,12 @@ gfw.ui.view.CountriesShow = cdb.core.View.extend({ var marginLeft = 40, marginTop = radius - h/2; - $amount.html(''+formatNumber(data[data.length - 1].alerts)+''); + //$amount.html(''+formatNumber(data[data.length - 1].alerts)+''); var date = new Date(data[data.length - 1].date), form_date = 'Alerts in ' + config.MONTHNAMES[date.getUTCMonth()] + ' ' + date.getUTCFullYear(); - $date.html(form_date); + //$date.html(form_date); var cx = width - 80 + marginLeft; var cy = h - y_scale(data[data.length - 1].alerts) + marginTop; @@ -587,12 +365,12 @@ gfw.ui.view.CountriesShow = cdb.core.View.extend({ var index = Math.round(x_scale.invert(d3.mouse(this)[0])); if (data[index]) { // if there's data - $amount.html(''+formatNumber(data[index].alerts)+''); + //$amount.html(''+formatNumber(data[index].alerts)+''); var date = new Date(data[index].date), form_date = 'Alerts in ' + config.MONTHNAMES[date.getUTCMonth()] + ' ' + date.getUTCFullYear(); - $date.html(form_date); + //$date.html(form_date); var cx = d3.mouse(this)[0] + marginLeft; var cy = h - y_scale(data[index].alerts) + marginTop; @@ -608,154 +386,10 @@ gfw.ui.view.CountriesShow = cdb.core.View.extend({ .attr('cx', cx) .attr('cy', cy) .attr('r', 5); - }); - } else if (type === 'bars') { - var sql = "SELECT "; - - if (options.dataset === 'loss') { - sql += "year, loss_gt_0 loss FROM umd WHERE iso='"+options.iso+"'"; - } else if (options.dataset === 'extent') { - sql += "year, extent_gt_25 extent FROM umd WHERE iso='"+options.iso+"'"; - } - - d3.json('https://wri-01.cartodb.com/api/v2/sql?q='+sql, function(json) { - if (json) { - $graph.removeClass('ghost'); - - var data = json.rows; - } else { - $coming_soon.show(); - - return; - } - - var data_ = []; - - _.each(data, function(val, key) { - if (val.year >= 2001) { - data_.push({ - 'year': val.year, - 'value': eval('val.'+options.dataset) - }); - } - }); - - $amount.html(''+formatNumber(parseInt(data_[data_.length - 1].value, 10))+''); - $date.html('Hectares in ' + data_[data_.length - 1].year); - - var marginLeft = 40, - marginTop = radius - h/2 + 5; - - var y_scale = d3.scale.linear() - .domain([0, d3.max(data_, function(d) { return parseFloat(d.value); })]) - .range([height, marginTop*2]); - - var barWidth = (width - 80) / data_.length; - - var bar = graph.selectAll('g') - .data(data_) - .enter() - .append('g') - .attr('transform', function(d, i) { return 'translate(' + (marginLeft + i * barWidth) + ','+ -marginTop+')'; }); - - bar.append('svg:rect') - .attr('class', function(d, i) { - if (i === 11) { // last year index - return 'last bar' - } else { - return 'bar' - } - }) - .attr('y', function(d) { return y_scale(d.value); }) - .attr('height', function(d) { return height - y_scale(d.value); }) - .attr('width', barWidth - 1) - .on('mouseover', function(d) { - d3.selectAll('.bar').style('opacity', '.5'); - d3.select(this).style('opacity', '1'); - - $amount.html(''+formatNumber(parseInt(d.value, 10))+''); - $date.html('Hectares in ' + d.year); - }); - }); - } else if (type === 'comp') { - var sql = 'SELECT y2001_y2012 as gain, (SELECT SUM('; - - for(var y = 2001; y < 2012; y++) { - sql += 'y'+y+' + '; - } - - sql += ['y2012) FROM countries_loss', - "WHERE iso = '"+options.iso+"') as loss", - 'FROM countries_gain', - "WHERE iso = '"+options.iso+"'"].join(' '); - - d3.json('https://wri-01.cartodb.com/api/v2/sql?q='+encodeURIComponent(sql), function(json) { - if (json) { - $graph.removeClass('ghost'); - - var data = json.rows[0]; - } else { - $coming_soon.show(); - - return; - } - - var data_ = [], - form_key = { - 'gain': 'Tree cover gain', - 'loss': 'Tree cover loss' - }; - - _.each(data, function(val, key) { - data_.push({ - 'key': form_key[key], - 'value': val - }); - }); - - $amount.html(''+formatNumber(parseInt(data_[data_.length - 1].value, 10))+''); - $date.html('Ha '+data_[data_.length - 1].key); - - var barWidth = (width - 80) / 12; - - var marginLeft = 40 + barWidth*5, - marginTop = radius - h/2 + 5; - - var y_scale = d3.scale.linear() - .domain([0, d3.max(data_, function(d) { return parseFloat(d.value); })]) - .range([height, marginTop*2]); - - var bar = graph.selectAll('g') - .data(data_) - .enter() - .append('g') - .attr('transform', function(d, i) { return 'translate(' + (marginLeft + i * barWidth) + ',' + -marginTop + ')'; }); - - bar.append('svg:rect') - .attr('class', function(d, i) { - if (i === 1) { // last bar index - return 'last bar' - } else { - return 'bar' - } - }) - .attr('y', function(d) { return y_scale(d.value); }) - .attr('height', function(d) { return height - y_scale(d.value); }) - .attr('width', barWidth - 1) - .style('fill', '#FFC926') - .style('shape-rendering', 'crispEdges') - .on('mouseover', function(d) { - d3.selectAll('.bar').style('opacity', '.5'); - d3.select(this).style('opacity', '1'); - - $amount.html(''+formatNumber(parseFloat(d.value).toFixed(1))+''); - $date.html('Ha '+d.key); - }); - }); - } + }); } -}); +}); gfw.ui.view.CountriesIndex = cdb.core.View.extend({ el: document.body, @@ -2795,4 +2429,4 @@ gfw.ui.view.CountriesEmbedShow = cdb.core.View.extend({ }); } } -}); +}); \ No newline at end of file diff --git a/app/assets/javascripts/stories.js b/app/assets/javascripts/stories.js index 507ef2f8a9..03da3d98ec 100644 --- a/app/assets/javascripts/stories.js +++ b/app/assets/javascripts/stories.js @@ -16,6 +16,7 @@ gfw.ui.view.StoriesEdit = cdb.core.View.extend({ }, initialize: function() { + console.log('ey'); _.bindAll(this, '_clickRemove'); this.model = new cdb.core.Model(); diff --git a/app/assets/stylesheets/countries.scss b/app/assets/stylesheets/countries.scss index 74875c432c..2e1f6a3dff 100644 --- a/app/assets/stylesheets/countries.scss +++ b/app/assets/stylesheets/countries.scss @@ -6,12 +6,14 @@ "compass/css3/images", "compass/css3/text-shadow", "compass/css3/inline-block", + "compass/utilities/sprites", "fonts", "helpers"; @import "country-icons/*.png"; +@include all-country-icons-sprites; -$primary-color: #A0B941; +$primary-color: #9FBA2B; $default-color: #463F52; $font-light: "fira_sans_otlight", Georgia, sans-serif; $font-regular: "fira_sans_otregular", Georgia, sans-serif; @@ -191,18 +193,25 @@ $font-medium: "fira_sans_otmedium", Georgia, sans-serif; /* =Show ----------------------------------------------- */ .country-show { - font-family: $font-medium; + font-family: $font-regular; + color: $default-color; } .country-header { width: 100%; - color: $default-color; border-bottom: 1px solid #CCCCCC; .country-header-inner { height: 350px; - background: #383643 image-url('backgrounds/wall_background_alpha.png'); - //@include background-size(cover); + $wall-background: "backgrounds/wall_background_alpha.png"; + background: #464253; + background-image: image-url($wall-background); /* fallback */ + background-image: image-url($wall-background), -webkit-gradient(linear, left top, left bottom, from(#464253), to(#383643)); /* Saf4+, Chrome */ + background-image: image-url($wall-background), -webkit-linear-gradient(top, #464253, #383643); /* Chrome 10+, Saf5.1+ */ + background-image: image-url($wall-background), -moz-linear-gradient(top, #464253, #383643); /* FF3.6+ */ + background-image: image-url($wall-background), -ms-linear-gradient(top, #464253, #383643); /* IE10 */ + background-image: image-url($wall-background), -o-linear-gradient(top, #464253, #383643); /* Opera 11.10+ */ + background-image: image-url($wall-background), linear-gradient(to bottom, #464253, #383643); /* W3C */ } .country-title { @@ -214,7 +223,7 @@ $font-medium: "fira_sans_otmedium", Georgia, sans-serif; max-width: 500px; margin-right: 20px; display: inline-block; - font-weight: normal; + font-family: $font-regular; } .country-selector { @@ -245,7 +254,7 @@ $font-medium: "fira_sans_otmedium", Georgia, sans-serif; .country-details { background-color:rgba(0, 0, 0, 0.2); - height: 261px; + height: 263px; .country-preview { width: 735px; @@ -281,6 +290,7 @@ $font-medium: "fira_sans_otmedium", Georgia, sans-serif; .amount, .unit { + margin-top: 6px; color: $primary-color; display: inline-block; } @@ -290,8 +300,8 @@ $font-medium: "fira_sans_otmedium", Georgia, sans-serif; font-weight: bold; } - .tree-cover { - margin-bottom: 25px; + .total-extension { + margin-top: 18px; } .total-extension .amount { @@ -317,12 +327,12 @@ $font-medium: "fira_sans_otmedium", Georgia, sans-serif; > li { height: 53px; width: 100%; - padding: 20px; + padding: 20px 25px; @include box-sizing(border-box); - font-weight: bold; + font-family: $font-medium; text-transform: uppercase; color: #464152; - font-size: 13px; + font-size: 14px; cursor: pointer; &:hover { @@ -341,7 +351,7 @@ $font-medium: "fira_sans_otmedium", Georgia, sans-serif; height: 185px; width: 100%; padding: 35px 20px; - color: $default-color; + font-family: $font-medium; @include box-sizing(border-box); //@include country-icons-sprite(country-indepth); @@ -358,18 +368,26 @@ $font-medium: "fira_sans_otmedium", Georgia, sans-serif; } .country-indepth__links { - margin-top: 20px; + margin-top: 25px; + color: #bbb; + font-family: $font-light; + font-size: 13px; a { + font-size: 14px; display: inline-block; vertical-align: top; color: $primary-color; - font-weight: 700; + font-family: $font-medium; } } } // country-indepth + .country-nav-container { + position: relative; + } + .country-nav { width: 100%; @@ -385,12 +403,11 @@ $font-medium: "fira_sans_otmedium", Georgia, sans-serif; width: 135px; float: left; margin-right: -1px; - font-size: 13px; - font-weight: 700; - color: #464052; + font-size: 14px; text-transform: uppercase; cursor: pointer; background: #F2F2F3; + font-family: $font-medium; &:first-child { width: 156px; @@ -412,12 +429,12 @@ $font-medium: "fira_sans_otmedium", Georgia, sans-serif; } .country-section { - border-bottom: 1px solid #CCCCCC; + border-bottom: 1px solid #E5E5E5; width: 100%; - padding: 40px 0; + padding: 45px 0; .section-info { - margin-bottom: 10px; + margin-bottom: 12px; font-size: 14px; color: $default-color; font-family: $font-medium; @@ -425,7 +442,7 @@ $font-medium: "fira_sans_otmedium", Georgia, sans-serif; letter-spacing: -0.2px; .info { - margin-right: 10px; + margin-right: 6px; } } @@ -448,7 +465,7 @@ $font-medium: "fira_sans_otmedium", Georgia, sans-serif; } .section-title { - font-size: 30px; + font-size: 29px; line-height: 36px; font-family: $font-medium; color: $default-color; @@ -456,33 +473,147 @@ $font-medium: "fira_sans_otmedium", Georgia, sans-serif; } } -.country-state { +.country-alerts { + .forma-alerts-legend { + margin: 5px 0 25px 0; + + span { + color: #666666; + font-size: 13px; + font-family: $font-light; + display: block; + } + + .legend-title { + color: $primary-color; + font-size: 14px; + font-family: $font-medium; + margin-bottom: 3px; + } + } + + .btn { + border-radius: 20px; + border: none; + padding: 10px 20px 8px; + background: $primary-color; + font-family: $font-regular; + font-size: 13px; + } +} + +.country-forests-type { + .forests-type-graph { + margin-top: -25px; + } + + .forest-type-legends { + font-size: 14px; + display: inline-block; + margin-top: 10px; + + .legends-title { + color: $primary-color; + margin-bottom: 10px; + display: block; + } + + .legends-list { + li { + display: block; + margin-bottom: 5px; + color: #777; + font-family: $font-light; + vertical-align: top; + + span { + height: 14px; + width: 14px; + border-radius: 100px; + display: inline-block; + margin-right: 4px; + } + + .regenerated { + background: #819515; + } + + .primary { + background: #A1BA42; + } + + .planted { + background: #DDDDDD; + } + } + } + } +} + +.country-tenure { + .line-graph { + font-weight: bold; + text-transform: uppercase; -} // country-state + circle { + stroke-width: 2px; + stroke: #f5f5f5; + } + + text { + @extend .sans-serif; + font-size: 12px; + } + + .one { + fill: #75B22E; + } + + .two { + fill: #AAC700; + } + + .three { + fill: #AC0; + } + + .four { + fill: #FFD24D; + } + } +} // country-tenure .country-employment { .section-content .left-col { width: 460px; } - .section-title { + .section-content .left-col .section-title { @extend .clearfix; margin-bottom: 10px; + font-size: 47px; + display: inline-block; div, span { + margin-top: 8px; float: left; } span { width: 250px; - margin: 10px 0 0 10px; - font-size: 13px; - line-height: 1.4; + margin: 4px 0 0 10px; + font-size: 11px; + line-height: 1.2; @extend .sans-serif; text-transform: uppercase; color: #444; } + + } + + .man-list { + color: #919191; } } diff --git a/app/views/countries/show_redesign.html.erb b/app/views/countries/show_redesign.html.erb index 0a98b292b4..673d1d90b1 100644 --- a/app/views/countries/show_redesign.html.erb +++ b/app/views/countries/show_redesign.html.erb @@ -1,3 +1,15 @@ +<% content_for :js do %> + +<% end %> +

    @@ -31,13 +43,19 @@

    LOSS AND GAIN (2001 - 2012)

    +
    +
    +
    @@ -56,16 +74,18 @@ <% end %> -
    -
      -
    • Forma clearing alerts
    • -
    • Forest type
    • -
    • State of the forest
    • -
    • People & Economy
    • -
    • Tenure & Laws
    • -
    • Climate Change
    • -
    • International agreements
    • -
    +
    @@ -82,9 +102,16 @@

    There were 7,363 FORMA alerts in october

    + +
    + Forest Clearing Alerts + Humid Tropics +
    + + Download data
    - +
    @@ -101,11 +128,20 @@

    - <%= @country['name'] %> has <%= number_to_human(@country['extent'], precision: 2) %> hectares of tree cover + <%= @country['name'] %> has <%= number_to_human(@country['extent'], precision: 2).downcase %> hectares of tree cover

    + +
    + Forest Type: +
      +
    • Regenerated
    • +
    • Primary
    • +
    • Planted
    • +
    +
    - +
    @@ -128,6 +164,7 @@ + <% if @employees.present? && @employees > 0 %>
    + <% end %>
    @@ -171,8 +207,7 @@
    -

    -

    +
    From 923a79754c5c95926b662e757102ecffe578737b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A1n=20P=C3=A9rez?= Date: Wed, 21 May 2014 19:14:24 +0200 Subject: [PATCH 006/823] protected areas: load geometry on analysis --- lib/assets/javascripts/gfw/ui/analysis.js.erb | 55 ++++++++++++++----- 1 file changed, 42 insertions(+), 13 deletions(-) diff --git a/lib/assets/javascripts/gfw/ui/analysis.js.erb b/lib/assets/javascripts/gfw/ui/analysis.js.erb index 201a23b7d1..e1e903b30d 100644 --- a/lib/assets/javascripts/gfw/ui/analysis.js.erb +++ b/lib/assets/javascripts/gfw/ui/analysis.js.erb @@ -269,9 +269,14 @@ gfw.ui.view.Analysis = cdb.core.View.extend({ }, loadPolygon: function(the_geom, options) { - var ha = 0, - title = ""; + var ha = 0, + title = "", + element_id; + if (options && options[0] !== 0 && !isNaN(options[0])) { + ha = formatNumber(options[0]); + element_id = options[1]; + } var style = config.OVERLAYSTYLES; style.editable = false; @@ -302,11 +307,13 @@ gfw.ui.view.Analysis = cdb.core.View.extend({ } else { title = "User defined area"; } - ha = this._calcAreaPolygon(the_geom); - if (isNaN(ha)) { - ha = formatNumber(Math.ceil((the_geom.features[0].properties['area']/10000) * 10) / 10, true); - } else { - ha = formatNumber(ha, true); + if (ha === 0) { + ha = this._calcAreaPolygon(the_geom); + if (isNaN(ha)) { + ha = formatNumber(Math.ceil((the_geom.features[0].properties['area']/10000) * 10) / 10, true); + } else { + ha = formatNumber(ha, true); + } } this.model.set('area', JSON.stringify(the_geom)); this.info.model.set('ha', ha); @@ -314,7 +321,7 @@ gfw.ui.view.Analysis = cdb.core.View.extend({ this._fitBounds(); - this._getAlertCount(); + this._getAlertCount(element_id); }, _fitBounds: function() { @@ -348,7 +355,7 @@ gfw.ui.view.Analysis = cdb.core.View.extend({ map.fitBounds(bounds); }, - _getAlertCount: function() { + _getAlertCount: function(elem) { var that = this; var area = this.model.get('area'), @@ -409,7 +416,7 @@ gfw.ui.view.Analysis = cdb.core.View.extend({ } - config.MAPOPTIONS.analysis = (not_analyze) ? '?'+range.substr(1) : '?geom='+encodeURI(area)+range; + config.MAPOPTIONS.analysis = (not_analyze) ? '?'+range.substr(1) : ((elem) ? '?'+elem+range : '?geom='+encodeURI(area)+range); if (config.ISO === 'ALL') { this.alertsUrl = baseurl+dataset+config.MAPOPTIONS.analysis; @@ -420,7 +427,7 @@ gfw.ui.view.Analysis = cdb.core.View.extend({ this.alertsUrl = baseurl+dataset+'?iso='+config.ISO+range; this.downloadUrl = baseurl+dataset+'.shp?iso='+config.ISO+range; } - +console.log(this.alertsUrl); if (not_analyze) { if (this.$el.find('.analysis_info').is(':visible')) { this.$el.find('.analysis_info').hide(); @@ -554,6 +561,28 @@ gfw.ui.view.Analysis = cdb.core.View.extend({ }); }, + loadProtectedAreaByWpaid: function(wdpaid,the_geom) { + var that = this; + + var query = "https://wri-01.cartodb.com/api/v2/sql?q=SELECT shape_area FROM wdpa_all WHERE wdpaid =" + wdpaid; + + $.ajax({ + url: query, + dataType: 'jsonp', + success: function(area) { + if (area.rows[0].shape_area) { + that.loadPolygon(the_geom, [area.rows[0].shape_area, 'protected_area=' + wdpaid]); + } + else{ + return 0; + } + }, + error: function(){ + return 'API: No area data'; + } + }); + }, + _setupDrawingManager: function() { var self = this; @@ -692,8 +721,8 @@ gfw.ui.view.Analysis = cdb.core.View.extend({ } }, - _loadProtectedArea: function(the_geom,area_id, area_name){ - this.loadPolygon(the_geom) + _loadProtectedArea: function(the_geom,area_id){ + this.loadProtectedAreaByWpaid(area_id, the_geom); }, render: function() { From 02215512913810f912be124235baa9493720195b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A1n=20P=C3=A9rez?= Date: Fri, 23 May 2014 12:35:54 +0200 Subject: [PATCH 007/823] umd layer: intensity options button --- app/assets/images/home-icons-s04c9890ba1.png | Bin 75215 -> 0 bytes app/assets/images/home-icons/icon_options.png | Bin 0 -> 682 bytes .../images/home-icons/icon_options_active.png | Bin 0 -> 705 bytes .../home-icons/icon_options_disable.png | Bin 0 -> 627 bytes .../images/home-icons/icon_options_hover.png | Bin 0 -> 687 bytes app/assets/images/icons-seb8bd12153.png | Bin 43779 -> 0 bytes app/assets/javascripts/countries.js | 1 + app/assets/javascripts/embed_map.js | 1 + app/assets/javascripts/home.js | 1 + app/assets/stylesheets/home.scss | 19 +++++++++ app/views/shared/_js_templates.html.erb | 13 ++++++- lib/assets/javascripts/gfw/gfw_lib.js.erb | 5 +++ lib/assets/javascripts/gfw/map.js | 8 ++++ lib/assets/javascripts/gfw/ui/share.js.erb | 3 +- .../javascripts/gfw/ui/umd_options.js.erb | 36 ++++++------------ 15 files changed, 58 insertions(+), 29 deletions(-) delete mode 100644 app/assets/images/home-icons-s04c9890ba1.png create mode 100644 app/assets/images/home-icons/icon_options.png create mode 100644 app/assets/images/home-icons/icon_options_active.png create mode 100644 app/assets/images/home-icons/icon_options_disable.png create mode 100644 app/assets/images/home-icons/icon_options_hover.png delete mode 100644 app/assets/images/icons-seb8bd12153.png diff --git a/app/assets/images/home-icons-s04c9890ba1.png b/app/assets/images/home-icons-s04c9890ba1.png deleted file mode 100644 index 76c55ea8614ac0934becd9b43b5bbce00beab056..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 75215 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 UdEBmJL*RAD+VS8EOYg{k0|r_#mH+?% diff --git a/app/assets/images/home-icons/icon_options.png b/app/assets/images/home-icons/icon_options.png new file mode 100644 index 0000000000000000000000000000000000000000..63913f269fe972097d354a689c63c410365bed8d GIT binary patch literal 682 zcmeAS@N?(olHy`uVBq!ia0vp^svyk43?x4}TrdPu%*9TgAsieWw;%dH0CMLC_=LFr z|Nq~|$0r~l07!<1hlhlO1P2F4L_}0oRh5*K^z`(!w6xUL)&jW&1qHdexj=SJO%0G+ zQBeU@02Bn8@cZ}gpFe+o{rdIu=g%KMeth@t-OHCRpFVwh|Ni~EckkZ5ef!3Z8&|Ge zxp?v7`Sa(`oH=vyY+5fCb*nO<_U}8@3oXxH`E*+P#GOV~0`t#2DcMo?RK62Jz z-i1Q7k9QZO9*T|fnffGbi)&?m&nNf&OF0eG1TGf4@13_s$>qO>{j?-6Ps6##iax+VWbkzLb6Mw<&;$VT$XqZ0 literal 0 HcmV?d00001 diff --git a/app/assets/images/home-icons/icon_options_active.png b/app/assets/images/home-icons/icon_options_active.png new file mode 100644 index 0000000000000000000000000000000000000000..5fff3956e454ff06f3280bac8c1b7eaa382da1a9 GIT binary patch literal 705 zcmeAS@N?(olHy`uVBq!ia0vp^svyk43?x4}TrdPu%*9TgAsieWw;%dH0CG15_=LFr z|NlRyPPnL51V~m-5UZIWR^BaI)+t&uS!}{`ss82CKyvaL*~x37XqqgEpP;S%1`Z^U1j!GWw51EJ_|%I108Q&666=mAZTpt0tBJEcb~Zn0`K0v z1A2%UP*ruq9%u|_fk$L90|U1Z2s2)~TlWVjC{yAZQ4*Y=R#Ki=l*-_klAn~S;FejG zTAp8&U98|7Z1!T$rXHX=5s*6PqSVBa%=|oskj&gv26KH=eM6H+pQki3FfdAZx;Tbd z^d6n^x~R!OpmpKa@SME`dm1%Zx2_0fj9s|zf3s=8sx2uKC6_<`IWcEazS(SFv#ui3 zl}4e7|B7D4e*RFYvFhi9!&i=1ZCSqPTj1WZLm|483_2gFe-)Xc_jyWHwv4l|2(#c; zy+t+^=^FJ?lbG8Nt&DeyG>~QJqOIc~fNPg>miLVQtt;gw~RpB$+44{AHt zwy*s( zYFtvL?Xk79Yi{1l&fU9U+q%2|{xPlkBP_E##y7s_d95A8y1R3LVaVX=>gTe~DWM4f D-Gp4! literal 0 HcmV?d00001 diff --git a/app/assets/images/home-icons/icon_options_disable.png b/app/assets/images/home-icons/icon_options_disable.png new file mode 100644 index 0000000000000000000000000000000000000000..4d13893afd35785a8d386b20587e7c79a938d0d7 GIT binary patch literal 627 zcmeAS@N?(olHy`uVBq!ia0vp^svyk43?x4}TrdPu%*9TgAsieWw;%dH0CHObd_r9R z|Nrmf;}Z}N03^f1!$U$sf`fx2A|fg(DvFAVIyyQU8X9VAYpbfNfFgN$c{w>b5O!Hv zS#fbOP)$KW0Z{zy+qW-YzI^`t`O~LQA3uKl@ZrPz_wV1md-v9@TQ_gsyng-q)vH%8 zU%q_e#EE0ajvYRH_~5~VyLRo$%F0ShOH1zYyA5=YPDzkoFoU46v5SjK=%^>g& z0)Xzs09z~@dV%^l3p^r=85p>QK$!8;-MT+OL75WQh?3y^w370~qErUQl>DSr1-Hzi z)bjkI>|zDyV6zu{HuV72iGb8O7o{eaWaj57gkKmFo`aGqPfq{|D)5S5w zqW9>OyG4f-1X`W5>yx6_=4L56F`d2`_50uZ>j{C260gQp>PfKevn(!aU#hWAZQ^Cs zGluGJ>t`>R@0`2ryRDB+PdK;bm87pGUNbbNOR8o3)d*U_e>9+KQ@`4gIgLx^d|mOq zKA7zp&r&H(-8EBstsE^UuqJToIs2BWCaTX}xT+$o{QJFMd21t2ZDHARcNyynQyx*9 zjNOYLoD8sv`a3hzOgt%<@4@Bk8!hHO;@1>yIymcsQN_{9%A$?2eKz;k=PD)Kubfx3 z)!JJ!C@8Q>^nB#2h|2ZBBLCmTU-Ibs)V;9E_zLgainYc*pZ}I!mcPBb=$~ak=+y1) j@ds4@vm6%(1hQ=fB*dX^Xu2IpFe;8`0?Yrckf=leEH&c5 zojG%6R#sM8S{n1m7#W~z%}Rp&f*Ayjja^(^LU*6J3jsi@-n|35057n3A#)UH0%w6o zWHAE+w-5+3Ub-L1;Fyx1l&avCS(I9yUzA;};2doBV$Y@? zpgIwdI_IL)#FEVXJcW?V+*Ag0eN%lylSiMYG%_$SN_o0ChFJ6-z4EfC$v}iP;nJ;E zy|TmJI-x9%OFaboYX664F9-=J*8Tf*W+UeZ-yNJP(@M8-1pk`htj>1+wpRS_n|86~ zGj{CcKUOlQ@Pk3d%ni)VD~l3?mD~G6J3jC~JvvE9MJ4%t z%c%uI44qDC0f(Q(HQZ-mdusB1*{{lvkuN?^34Lh9E~dAWTOq!!N2^r6^6n)m)x~LC z5m(oWE=Y@s6*_yv$YImf-)A4JTKH?utdLg4&Q+SumWfkzLT>qlui`qn_oC{r#LTKtah{7zKW(1$)R$Fi_v916P-O6Q^>bP0l+XkK=G9nG literal 0 HcmV?d00001 diff --git a/app/assets/images/icons-seb8bd12153.png b/app/assets/images/icons-seb8bd12153.png deleted file mode 100644 index 2f665bb11b71b80c69a984bceebbb6c1d23250bb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 43779 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 - + + \ No newline at end of file diff --git a/lib/assets/javascripts/gfw/gfw_lib.js.erb b/lib/assets/javascripts/gfw/gfw_lib.js.erb index 06da1b0267..194de6bf81 100644 --- a/lib/assets/javascripts/gfw/gfw_lib.js.erb +++ b/lib/assets/javascripts/gfw/gfw_lib.js.erb @@ -1540,6 +1540,11 @@ GFW.modules.maplayer = function(gfw) { } else { Analysis.show(); } + if (slug === 'loss' || GFW.app.currentBaseLayer === 'loss') { + UmdOptions.show(); + } else { + UmdOptions.hide(); + } updateHash(); } }); diff --git a/lib/assets/javascripts/gfw/map.js b/lib/assets/javascripts/gfw/map.js index 01f897e076..07dce7eb05 100644 --- a/lib/assets/javascripts/gfw/map.js +++ b/lib/assets/javascripts/gfw/map.js @@ -13,6 +13,7 @@ var loaded = false, Legend = {}, Analysis = {}, SearchBox = {}, + UmdOptions = {}, Timeline = {}, resizePID; // Filter already init @@ -228,6 +229,10 @@ $(function() { this.$map.append(Analysis.render()); Analysis.info.setDraggable(true); + // UMD options + UmdOptions = new gfw.ui.view.UmdOptions({ map: map }); + this.$map.append(UmdOptions.render()); + // Share Share = new gfw.ui.view.Share(); if (!$('body').hasClass('embed')) { @@ -315,6 +320,9 @@ $(function() { Legend.show(); SearchBox.show(); Share.show(); + if (config.BASELAYER === 'loss') { + UmdOptions.show(); + } if ($('body').hasClass('embed')) { Analysis.disableAnalysisButton(); diff --git a/lib/assets/javascripts/gfw/ui/share.js.erb b/lib/assets/javascripts/gfw/ui/share.js.erb index 5467db2586..558b7e7686 100644 --- a/lib/assets/javascripts/gfw/ui/share.js.erb +++ b/lib/assets/javascripts/gfw/ui/share.js.erb @@ -63,7 +63,6 @@ gfw.ui.view.Share = cdb.core.View.extend({ render: function() { var that = this; - this.$el.append(this.template.render( this.model.toJSON() )); this.$analysis_control = (this.$el.find('.analysis_control').length > 0) ? this.$el.find('.analysis_control') : null; if (!!this.$analysis_control) { @@ -234,7 +233,7 @@ gfw.ui.view.ShareDialog = gfw.ui.view.Widget.extend({ _setMode: function(mode) { this.model.set({ mode: mode }); }, - + _toggleMode: function() { var mode = this.model.get('mode'); diff --git a/lib/assets/javascripts/gfw/ui/umd_options.js.erb b/lib/assets/javascripts/gfw/ui/umd_options.js.erb index be84fdbce7..42f2d9301e 100644 --- a/lib/assets/javascripts/gfw/ui/umd_options.js.erb +++ b/lib/assets/javascripts/gfw/ui/umd_options.js.erb @@ -1,6 +1,6 @@ gfw.ui.model.UmdOptions = cdb.core.Model.extend({ defaults: { - tooltip: $('body').hasClass('home') ? "Share this map view" : "Share this graphic" + tooltip: "Options" } }); @@ -14,11 +14,11 @@ gfw.ui.view.UmdOptions = cdb.core.View.extend({ initialize: function() { _.bindAll( this, '_toggle' ); - this.model = new gfw.ui.model.Share(); + this.model = new gfw.ui.model.UmdOptions(); this.model.on('change:hidden', this._toggle); - - var template = $('#umd-options-template').html(); + + var template = $('#umd_options-template').html(); this.template = new cdb.core.Template({ template: template, @@ -29,12 +29,7 @@ gfw.ui.view.UmdOptions = cdb.core.View.extend({ }, _initViews: function() { - this.umdoptions = new gfw.ui.view.umdoptionsDialog({ - bitly: { - key: "<%= ENV['BITLY_API_KEY'] %>", - login: 'vizzuality' - } - }); + this.umdoptions = new gfw.ui.view.umdoptionsDialog({}); $('body').append(this.umdoptions.render()); }, @@ -55,19 +50,19 @@ gfw.ui.view.UmdOptions = cdb.core.View.extend({ show: function() { this.model.set('hidden', false); + $('.share_control').addClass('umd_options') }, hide: function() { this.model.set('hidden', true); + $('.share_control').removeClass('umd_options') }, render: function() { - var that = this; - this.$el.append(this.template.render( this.model.toJSON() )); - this.$analysis_control = (this.$el.find('.analysis_control').length > 0) ? this.$el.find('.analysis_control') : null; - if (!!this.$analysis_control) { - $(this.$analysis_control).tipsy({ title: 'data-tip', fade: true, gravity: 'w' }); + this.$umd_options_control = (this.$el.find('.umd_options-control').length > 0) ? this.$el.find('.umd_options-control') : null; + if (!!this.$umd_options_control) { + $(this.$umd_options_control).tipsy({ title: 'data-tip', fade: true, gravity: 'w' }); } return this.$el; @@ -77,11 +72,6 @@ gfw.ui.view.UmdOptions = cdb.core.View.extend({ gfw.ui.model.umdoptionsDialog = Backbone.Model.extend({ defaults: { - title: $('body').hasClass('home') ? "Share this map view" : "Share this graphic", - help: "Click and paste link in email or IM", - button_title: "Copy URL", - hidden: false, - mode: "link" } }); @@ -161,17 +151,15 @@ gfw.ui.view.umdoptionsDialog = gfw.ui.view.Widget.extend({ // Disable textarea this.$input.val(''); - this.$loading.show(); // Get link short this._generateShortUrl(window.location.href, function(url) { that.$input.val(url); - + that.$share.find('.twitter').attr('href', "https://twitter.com/share?url=" + url); that.$share.find('.facebook').attr('href', "https://www.facebook.com/sharer.php?u=" + url); that.$share.find('.google_plus').attr('href', "https://plus.google.com/share?url=" + url); - that.$loading.hide(); }); }, @@ -259,8 +247,6 @@ gfw.ui.view.umdoptionsDialog = gfw.ui.view.Widget.extend({ }, render: function() { - var that = this; - this.$el.append(this.template.render( this.model.toJSON() )); this.$help = this.$el.find('.help span'); From 75ff9332b088240677fd59dda527ba6df16b3f2c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A1n=20P=C3=A9rez?= Date: Fri, 23 May 2014 12:37:14 +0200 Subject: [PATCH 008/823] umd layer: intensity options sprite images --- app/assets/images/home-icons-s5aad8d2020.png | Bin 0 -> 78722 bytes app/assets/images/icons-s6c5f3ef246.png | Bin 0 -> 47597 bytes 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 app/assets/images/home-icons-s5aad8d2020.png create mode 100644 app/assets/images/icons-s6c5f3ef246.png diff --git a/app/assets/images/home-icons-s5aad8d2020.png b/app/assets/images/home-icons-s5aad8d2020.png new file mode 100644 index 0000000000000000000000000000000000000000..fb6750d5e323fc2d793cc3c6d104b2f5cea1f6f6 GIT binary patch literal 78722 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 z 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 ubZ((5RM?-DJk7u(!a~+ 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 Date: Fri, 23 May 2014 12:38:35 +0200 Subject: [PATCH 009/823] country map & forest layer --- .../images/country-icons-s188448b620.png | Bin 8345 -> 0 bytes .../images/country-icons-sb41a0ffadc.png | Bin 0 -> 8333 bytes .../images/country-icons/chart_inverse.png | Bin 461 -> 448 bytes .../images/country-icons/download_inverse.png | Bin 282 -> 325 bytes .../images/country-icons/info_inverse.png | Bin 563 -> 569 bytes app/assets/javascripts/countries.js | 2425 +---------------- .../countries/models/countries_overview.js | 7 + .../javascripts/countries/models/country.js | 24 + .../javascripts/countries/views/embed.js | 358 +++ .../javascripts/countries/views/index.js | 37 + .../javascripts/countries/views/overview.js | 1631 +++++++++++ .../javascripts/countries/views/show.js | 472 ++++ app/assets/stylesheets/countries.scss | 1254 +-------- app/assets/stylesheets/countries/index.scss | 97 + .../stylesheets/countries/overview.scss | 697 +++++ app/assets/stylesheets/countries/show.scss | 470 ++++ app/controllers/countries_controller.rb | 2 + app/views/countries/show_redesign.html.erb | 59 +- app/views/layouts/application.html.erb | 1 + 19 files changed, 3848 insertions(+), 3686 deletions(-) delete mode 100644 app/assets/images/country-icons-s188448b620.png create mode 100644 app/assets/images/country-icons-sb41a0ffadc.png create mode 100644 app/assets/javascripts/countries/models/countries_overview.js create mode 100644 app/assets/javascripts/countries/models/country.js create mode 100644 app/assets/javascripts/countries/views/embed.js create mode 100644 app/assets/javascripts/countries/views/index.js create mode 100644 app/assets/javascripts/countries/views/overview.js create mode 100644 app/assets/javascripts/countries/views/show.js create mode 100644 app/assets/stylesheets/countries/index.scss create mode 100644 app/assets/stylesheets/countries/overview.scss create mode 100644 app/assets/stylesheets/countries/show.scss diff --git a/app/assets/images/country-icons-s188448b620.png b/app/assets/images/country-icons-s188448b620.png deleted file mode 100644 index 0d7fc003d48f13d006d7b6ae1b6e85eef723a613..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8345 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 diff --git a/app/assets/images/country-icons-sb41a0ffadc.png b/app/assets/images/country-icons-sb41a0ffadc.png new file mode 100644 index 0000000000000000000000000000000000000000..6489b992accfb90eee09379eac341cb330aebdc6 GIT binary patch literal 8333 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 Z8~{XV{NR0QD9iu=002ovPDHLkV1krOzF+_V diff --git a/app/assets/images/country-icons/download_inverse.png b/app/assets/images/country-icons/download_inverse.png index 88775141a51fbc2147d1992278028a8f370624f7..afa53c5c9c7cbd6eb2c65a48af8fa06fe8f122e2 100644 GIT binary patch delta 262 zcmbQmbd<@hGr-TCmrII^fq{Y7)59eQNDF~52OE$KJJ%Nuq*#ibJVQ8upoSx*1IXtr z@Q5sCVBmTU!i-JbyipU=v?qEf*3a{FaSXBOP5$%$zdf@lgK(%%VnRZKqj*9}O3UWn zFKYwlRKI&<=oVwQU{<#q@1n(y5*IUb67C*6a3H|X;eO9%@xHW2r5AU0AJ>wQkg)nF zdFa4_39Wh^SFy>HG+{{Q#)v{n5}i)%}IuPdk={$cX} z|Nr@=|1N#}zhe`}wgYObEG()YYIOBl7%Q$!_@L^+ugf#Gr-TCmrII^fq{Y7)59eQNQ2oNOhA@=Xa-1tx!B1wgoA_Q_Cx;%KrUy2 zM`SSr1Gf+eGhVt|_h(|B%0y3%`VvnU#}J9B$$$R;w`T@{=8cV}%sg}U7Kj{5NIJmH zw$bGA63)wFXX2Ye^;Aq6DsO+&-Cy@_SHVM}WihUyoWjh^&UtldPDwn=WR#Va57$e~ zFiD&>ao^O5`$D6=ukWw_uh7Ji*};{v^Z)<<#!@e%9~N3NoIT>*i_@>>j8p`4O)?ADv?Jae*uz7L_t(IjjfZtOIMQ2AHdfzcf}~Khz)o-|PHaN(CGg2uU3JdU z;Fa%ZeQmqLvJ-|A%m8k43%?M1r6+oVueKwFOSS!!ggj)B9BPysl}F?PUy3QB4f zxDSF=WAy^CUI+BUSY3GmGFD#$Ux6ntSd>Cd@p?1>%E01tll9lefj!`39SeXXVBT2$ z0QtYaD;1ch_S^$N2eo-R1$K?qr+z*Ky+{2A)NYb{QiXqq00000NkvXXu0mjfrl`=Y delta 503 zcmVKC z3JUW56FZfa6``u{-n}0io&4p?S4^c4z{A76ciht4ZrB15_4D^CD=+-?@zeeL5B2qRl~t5B zZQKfx`taeSgoGH&jvag8Hf~tI<>iZ4r%#{z^XJdW(+4CZ#F5>zW5*t*ix(~<3mrdx z>fgVAb@i2;oSa7vA48V9c;PbBj~_qK{FIxQ5fU1IaA5!8p6-dr;y-@;VB+B5KvV7N z>h$yHuhNpLKY#upi*s;ru;?4;o;-0HSu`)Vgo&B?=g(hgrsx~!GI@Bopot0#3keGF zGhhG@4;RKifBppeM 0) { - // this.indepth_bar = 48; - - // h_min -= 48; - // h_max -= 48; - - // if ($(window).scrollTop() > (h_min) && $(window).scrollTop() < h_max) { - // this.$indepth.css({ - // position: 'fixed', - // top: 0 - // }); - // } else if ($(window).scrollTop() >= h_max) { - // this.$indepth.css({ - // position: 'absolute', - // top: h_max - h_min - // }); - // } else { - // this.$indepth.css({ - // position: 'absolute', - // top: 0 - // }); - // } - // } - - if ($(window).scrollTop() > (h_min) && $(window).scrollTop() < h_max) { - this.$nav.css({ - position: 'fixed', - top: this.indepth_bar - }); - } else if ($(window).scrollTop() >= h_max) { - this.$nav.css({ - position: 'absolute', - top: h_max - h_min - }); - } else { - this.$nav.css({ - position: 'absolute', - top: 0 - }); - } - }, - - _stickynav: function() { - this._positionScroll(); - - $.scrollIt({ - upKey: null, - downKey: null, - easing: 'linear', - scrollTime: 600, - activeClass: 'active', - onPageChange: null, - topOffset: -48 - this.indepth_bar - }); - - $(window).scroll(this._positionScroll); - }, - - _drawTenure: function(iso) { - var sql = ['SELECT tenure_government, tenure_owned, tenure_owned_individuals,', - 'tenure_reserved, GREATEST(tenure_government, tenure_owned,', - 'tenure_owned_individuals,', - 'tenure_owned_individuals,', - 'tenure_reserved) as max', - "FROM gfw2_countries WHERE iso = '"+iso+"'"].join(' '); - - d3.json('https://wri-01.cartodb.com/api/v2/sql?q='+sql, function(json) { - var data = json.rows[0], - h = 0; - - var x_extent = [0, data.max], - x_scale = d3.scale.linear() - .range([0, 500]) - .domain(x_extent); - - var origins = [], - aggr = 0, - klass = ['one', 'two', 'three', 'four'] - - var tenures = [ - { - name: 'Public lands administered by the government', - percent: data.tenure_government - }, - { - name: 'Public lands reserved for communities and indigenous groups', - percent: data.tenure_reserved - }, - { - name: 'Private lands owned by communities and indigenous groups', - percent: data.tenure_owned - }, - { - name: 'Private lands owned by firms and individuals', - percent: data.tenure_owned_individuals - } - ]; - - var tenures_ord = []; - - _.each(tenures, function(tenure, i) { - if (tenure['percent'] !== null && tenure['percent'] !== 0) { - if ($('.country-tenure').not(':visible')) { - $('.country-tenure').show(); - } - - h += 50; - - tenures_ord.push({ - name: tenure['name'], - percent: tenure['percent'] - }); - } - }); - - var svg = d3.select('.country-tenure .line-graph') - .append('svg') - .attr('width', 600) - .attr('height', h); - - // add lines - svg.selectAll('rect') - .data(tenures_ord) - .enter() - .append('rect') - .attr('class', function(d, i) { - return klass[i]; - }) - .attr('x', function() { - return x_scale(0); - }) - .attr('y', function(d, i) { - return 25 + (50 * i); - }) - .attr('width', function(d) { - return x_scale(d['percent']); - }) - .attr('height', 4) - .attr('rx', 2) - .attr('ry', 2); - - // add balls - svg.selectAll('circle') - .data(tenures_ord) - .enter() - .append('svg:circle') - .attr('class', function(d, i) { - return klass[i]; - }) - .attr('cx', function(d, i) { - return x_scale(d['percent']); - }) - .attr('cy', function(d, i) { - return 27 + (50 * i); - }) - .attr('r', 5); - - // add values - svg.selectAll('.units') - .data(tenures_ord) - .enter() - .append('text') - .text(function(d) { - return d['percent']/1000000 + 'Mha'; - }) - .attr('class', function(d, i) { - return 'units ' + klass[i]; - }) - .attr('x', function(d, i) { - return x_scale(d['percent'])+10; - }) - .attr('y', function(d, i) { - return 31 + (50 * i); - }); - - // add legend - svg.selectAll('.legend') - .data(tenures_ord) - .enter() - .append('text') - .text(function(d) { - return d['name']; - }) - .attr('class', function(d, i) { - return 'legend ' + klass[i]; - }) - .attr('x', 0) - .attr('y', function(d, i) { - return 15 + (50 * i); - }); - }); - }, - - _drawForestsType: function(iso) { - var sql = ["SELECT unnest(array['forest_regenerated', 'forest_primary', 'forest_planted'])", - 'AS type, unnest(array[COALESCE(forest_regenerated, 0),', - 'COALESCE(forest_primary, 0),', - 'COALESCE(forest_planted, 0)])', - 'AS percent', - 'FROM gfw2_countries', - "WHERE iso = '"+iso+"'"].join(' '); - - d3.json('https://wri-01.cartodb.com/api/v2/sql?q='+sql, function(json) { - // TODO => if percents are 0 - var data = _.pluck(json.rows, 'percent'); - - var width = 225, - height = 225, - radius = Math.min(width, height) / 2, - colors = ['#819515', '#A1BA42', '#DDDDDD'], - labelColors = ['white', 'white', '#555']; - - var pie = d3.layout.pie() - .sort(null); - - var arc = d3.svg.arc() // create elements for using arc data - .innerRadius(radius - 67) - .outerRadius(radius) - - var svg = d3.select(".forests-type-graph") - .append("svg") - .attr("width", width) - .attr("height", height) - .append("g") - .attr("transform", "translate(" + width / 2 + "," + height / 2 + ")"); - - var path = svg.selectAll("path") - .data(pie(data)); - - path.enter().append("path") - .attr("fill", function(d, i) { return colors[i]; }) - .attr("d", arc); - - path.enter().append('text') - .attr('transform', function(d) { var c = arc.centroid(d); return 'translate(' + (c[0]-12) + ',' + (c[1]+8) + ')'}) - .text(function(d) { return d.data + '%' }) - .attr('fill', function(d, i) { return labelColors[i]; } ) - .style('font-size', '13px'); - }); - }, - - _drawFormaAlerts: function(iso) { - var that = this; - - var $graph = $('.forma-graph'); - - var width = 500, - height = 156, - h = 156, // maxHeight - radius = width / 2, - gridLinesCount = 7; - - // Add dashed grid - var graph = d3.select('.forma-graph') - .append('svg:svg') - .attr('class', 'line') - .attr('width', width) - .attr('height', height); - - var gridLineY = height; - for (var i = 0; i < gridLinesCount; i++) { - graph.append('svg:line') - .attr('x1', 0) - .attr('y1', gridLineY) - .attr('x2', width) - .attr('y2', gridLineY) - .style('stroke-dasharray', ('2, 3')) - .style('stroke', function() { return (i == 0) ? '#333' : '#CCC'; } ); - - gridLineY -= height/(gridLinesCount-1); - }; - - // Render forma graph - var sql = ["SELECT date_trunc('month', date) as date, COUNT(*) as alerts", - 'FROM forma_api', - "WHERE iso = '"+iso+"'", - "GROUP BY date_trunc('month', date)", - "ORDER BY date_trunc('month', date) ASC"].join(' '); - - d3.json('https://wri-01.cartodb.com/api/v2/sql?q='+sql, function(json) { - if (json && json.rows.length > 0) { - var data = json.rows.slice(1, json.rows.length); - console.log(data); - } else { - console.log('no data'); - //$coming_soon.show(); - return; - }; - - var x_scale = d3.scale.linear() - .domain([0, data.length - 1]) - .range([0, width - 80]); - - var max = d3.max(data, function(d) { return parseFloat(d.alerts); }); - - if (max === d3.min(data, function(d) { return parseFloat(d.alerts); })) { - h = h/2; - } - - var y_scale = d3.scale.linear() - .domain([0, max]) - .range([0, h]); - - var line = d3.svg.line() - .x(function(d, i) { return x_scale(i); }) - .y(function(d, i) { return h - y_scale(d.alerts); }) - .interpolate('basis'); - - var marginLeft = 40, - marginTop = radius - h/2; - - //$amount.html(''+formatNumber(data[data.length - 1].alerts)+''); - - var date = new Date(data[data.length - 1].date), - form_date = 'Alerts in ' + config.MONTHNAMES[date.getUTCMonth()] + ' ' + date.getUTCFullYear(); - - //$date.html(form_date); - - var cx = width - 80 + marginLeft; - var cy = h - y_scale(data[data.length - 1].alerts) + marginTop; - - graph.append('svg:path') - .attr('transform', 'translate(' + marginLeft + ',' + marginTop + ')') - .attr('d', line(data)) - .on('mousemove', function(d) { - var index = Math.round(x_scale.invert(d3.mouse(this)[0])); - - if (data[index]) { // if there's data - //$amount.html(''+formatNumber(data[index].alerts)+''); - - var date = new Date(data[index].date), - form_date = 'Alerts in ' + config.MONTHNAMES[date.getUTCMonth()] + ' ' + date.getUTCFullYear(); - - //$date.html(form_date); - - var cx = d3.mouse(this)[0] + marginLeft; - var cy = h - y_scale(data[index].alerts) + marginTop; - - graph.select('.forma_marker') - .attr('cx', cx) - .attr('cy', cy); - } - }); - - graph.append('svg:circle') - .attr('class', 'forma_marker') - .attr('cx', cx) - .attr('cy', cy) - .attr('r', 5); - }); - } - -}); - -gfw.ui.view.CountriesIndex = cdb.core.View.extend({ - el: document.body, - - initialize: function() { - this._drawCountries(); - }, - - _drawCountries: function() { - var that = this; - - var sql = ['SELECT c.iso, c.enabled, m.the_geom', - 'FROM ne_50m_admin_0_countries m, gfw2_countries c', - 'WHERE c.iso = m.adm0_a3 AND c.enabled', - '&format=topojson'].join(' '); - - var sql_ = ['SELECT c.iso, m.the_geom', - 'FROM ne_50m_admin_0_countries m, gfw2_countries c', - 'WHERE c.iso = m.adm0_a3', - "AND c.iso = 'TWN'&format=topojson"].join(' '); - - d3.json('https://wri-01.cartodb.com/api/v2/sql?q='+sql, function(error, topology) { - for (var i = 0; i < Object.keys(topology.objects).length; i++) { - var iso = topology.objects[i].properties.iso; - - var bounds = draw(topology, i, iso, { alerts: false }); - - if (iso === 'CHN') { - that.bounds = bounds; - - d3.json('https://wri-01.cartodb.com/api/v2/sql?q='+sql_, function(error, topology) { - draw(topology, 0, 'CHN', { alerts: false, bounds: that.bounds}); - }); - } - } - }); - } -}); - - -gfw.ui.model.CountriesOverview = cdb.core.Model.extend({ - defaults: { - graph: 'total_loss', - years: true, - class: null - } -}); - - -gfw.ui.view.CountriesOverview = cdb.core.View.extend({ - el: document.body, - - events: { - 'click .info': '_openSource', - 'click .graph_tab': '_updateGraph', - 'click .countries_list__footer': '_drawList' - }, - - initialize: function() { - this.model = new gfw.ui.model.CountriesOverview(); - - this.$graph = $('.overview_graph__area'); - this.$years = $('.overview_graph__years'); - - var m = this.m = 40, - w = this.w = this.$graph.width()+(m*2), - h = this.h = this.$graph.height(), - vertical_m = this.vertical_m = 20; - - this.x_scale = d3.scale.linear() - .range([m, w-m]) - .domain([2001, 2012]); - - this.grid_scale = d3.scale.linear() - .range([vertical_m, h-vertical_m]) - .domain([0, 1]); - - this.model.bind('change:graph', this._redrawGraph, this); - this.model.bind('change:years', this._toggleYears, this); - this.model.bind('change:class', this._toggleClass, this); - - this._initViews(); - }, - - _initViews: function() { - this.sourceWindow = new gfw.ui.view.SourceWindow(); - this.$el.append(this.sourceWindow.render()); - - this.tooltip = d3.select('body') - .append('div') - .attr('class', 'tooltip'); - - this._drawYears(); - this._drawGraph(); - this._drawList(); - - Share = new gfw.ui.view.Share(); - this.$el.find('.overview_graph').append(Share.render()); - }, - - _openSource: function(e) { - e.preventDefault(); - - var source = $(e.target).closest('.info').attr('data-source'); - - ga('send', 'event', 'SourceWindow', 'Open', source); - this.sourceWindow.show(source).addScroll(); - }, - - _toggleYears: function() { - var that = this; - - if (this.model.get('years') === false) { - this.$years.slideUp(250, function() { - $('.overview_graph__axis').slideDown(); - }); - } else { - $('.overview_graph__axis').slideUp(250, function() { - that.$years.slideDown(); - }); - } - }, - - _showYears: function() { - if (!this.model.get('years')) { - this.model.set('years', true); - } - }, - - _hideYears: function() { - if (this.model.get('years')) { - this.model.set('years', false); - } - }, - - _updateGraph: function(e) { - e.preventDefault(); - - var $target = $(e.target).closest('.graph_tab'), - graph = $target.attr('data-slug'); - - if (graph === this.model.get('graph')) { - return; - } else { - $('.graph_tab').removeClass('selected'); - $target.addClass('selected'); - - this.model.set('graph', graph); - } - }, - - _redrawGraph: function() { - var graph = this.model.get('graph'); - - $('.overview_graph__title').html(config.GRAPHS[graph].title); - $('.overview_graph__legend p').html(config.GRAPHS[graph].subtitle); - $('.overview_graph__legend .info').attr('data-source', graph); - - this.$graph.find('.'+graph); - - this.$graph.find('.chart').hide(); - this.$graph.find('.'+graph).fadeIn(); - - this._drawGraph(); - this._drawList(); - }, - - _drawList: function(e) { - var that = this; - - e && e.preventDefault(); - - if (this.model.get('graph') === 'total_loss') { - var sql = 'WITH loss as (SELECT iso, SUM('; - - for(var y = 2001; y < 2012; y++) { - sql += 'y'+y+' + '; - } - - sql += 'y2012) as sum_loss\ - FROM countries_loss\ - GROUP BY iso)'; - - sql += 'SELECT c.iso, c.name, c.enabled, sum_loss\ - FROM loss, gfw2_countries c\ - WHERE loss.iso = c.iso\ - AND NOT sum_loss = 0\ - ORDER BY sum_loss DESC '; - - if (e) { - sql += 'OFFSET 10'; - } else { - sql += 'LIMIT 10'; - } - - d3.json('http://wri-01.cartodb.com/api/v2/sql/?q='+encodeURIComponent(sql), function(json) { - var self = that, - markup_list = ''; - - var data = json.rows; - - _.each(data, function(val, key) { - var ord = e ? (key+11) : (key+1), - enabled = val.enabled ? ''+val.name+'' : val.name; - - markup_list += '
  • \ -
    \ -
    '+ord+'
    \ -
    '+enabled+'
    \ -
  • '; - }); - - if (e) { - $('.countries_list__footer').fadeOut(); - } else { - $('.countries_list ul').html(''); - $('.countries_list__footer').show(); - - $('.countries_list__header__minioverview').html('Loss vs Gain'); - } - - $('.countries_list ul').append(markup_list); - - that.model.set('class', null); - - _.each(data, function(val, key) { - self._drawMiniOverview(val.iso); - }); - }); - } else if (this.model.get('graph') === 'percent_loss') { - var sql = 'SELECT c.iso, c.name, c.enabled, loss_y2001_y2012 as ratio_loss\ - FROM countries_percent percent, gfw2_countries c\ - WHERE percent.iso = c.iso AND c.enabled IS true\ - AND NOT loss_y2001_y2012 = 0\ - ORDER BY ratio_loss DESC '; - - if (e) { - sql += 'OFFSET 10'; - } else { - sql += 'LIMIT 10'; - } - - d3.json('http://wri-01.cartodb.com/api/v2/sql/?q='+encodeURIComponent(sql), function(json) { - var self = that, - markup_list = ''; - - var data = json.rows; - - _.each(data, function(val, key) { - var ord = e ? (key+11) : (key+1), - enabled = val.enabled ? ''+val.name+'' : val.name; - - markup_list += '
  • \ -
    \ -
    '+ord+'
    \ -
    '+enabled+'
    \ -
  • '; - }); - - if (e) { - $('.countries_list__footer').fadeOut(); - } else { - $('.countries_list ul').html(''); - $('.countries_list__footer').show(); - - $('.countries_list__header__minioverview').html('% Loss'); - } - - $('.countries_list ul').append(markup_list); - - that.model.set('class', null); - - _.each(data, function(val, key) { - self._drawMiniOverview(val.iso); - }); - }); - } else if (this.model.get('graph') === 'total_extent') { - var sql = 'WITH extent as (SELECT iso, SUM('; - - for(var y = 2001; y < 2012; y++) { - sql += 'y'+y+' + '; - } - - sql += 'y2012) as sum_extent\ - FROM extent_gt_25\ - GROUP BY iso)'; - - sql += 'SELECT c.iso, c.name, c.enabled, sum_extent\ - FROM extent, gfw2_countries c\ - WHERE extent.iso = c.iso\ - AND NOT sum_extent = 0\ - ORDER BY sum_extent DESC '; - - if (e) { - sql += 'OFFSET 10'; - } else { - sql += 'LIMIT 10'; - } - - d3.json('http://wri-01.cartodb.com/api/v2/sql/?q='+encodeURIComponent(sql), function(json) { - var self = that, - markup_list = ''; - - var data = json.rows; - - _.each(data, function(val, key) { - var ord = e ? (key+11) : (key+1), - enabled = val.enabled ? ''+val.name+'' : val.name; - - markup_list += '
  • \ -
    \ -
    '+ord+'
    \ -
    '+enabled+'
    \ -
  • '; - }); - - if (e) { - $('.countries_list__footer').fadeOut(); - } else { - $('.countries_list ul').html(''); - $('.countries_list__footer').show(); - - $('.countries_list__header__minioverview').html('Cover extent vs Cover loss'); - } - - $('.countries_list ul').append(markup_list); - - that.model.set('class', 'expanded'); - - _.each(data, function(val, key) { - self._drawMiniOverview(val.iso); - }); - }); - } else if (this.model.get('graph') === 'ratio') { - var sql = 'WITH loss as (SELECT iso, SUM('; - - for(var y = 2001; y < 2012; y++) { - sql += 'loss.y'+y+' + '; - } - - sql += 'loss.y2012) as sum_loss\ - FROM loss_gt_50 loss\ - GROUP BY iso), gain as (SELECT g.iso, SUM(y2001_y2012) as sum_gain\ - FROM countries_gain g, loss_gt_50 loss\ - WHERE loss.iso = g.iso\ - GROUP BY g.iso), ratio as ('; - - sql += 'SELECT c.iso, c.name, c.enabled, loss.sum_loss/gain.sum_gain as ratio\ - FROM loss, gain, gfw2_countries c\ - WHERE sum_gain IS NOT null\ - AND NOT sum_gain = 0\ - AND c.iso = gain.iso\ - AND c.iso = loss.iso\ - ORDER BY loss.sum_loss DESC\ - LIMIT 50) '; - - sql += 'SELECT *\ - FROM ratio\ - WHERE ratio IS NOT null\ - ORDER BY ratio DESC '; - - if (e) { - sql += ['OFFSET 10', - 'LIMIT 40'].join('\n'); - } else { - sql += 'LIMIT 10'; - } - - d3.json('http://wri-01.cartodb.com/api/v2/sql/?q='+encodeURIComponent(sql), function(json) { - var self = that, - markup_list = ''; - - var data = json.rows; - - _.each(data, function(val, key) { - var ord = e ? (key+11) : (key+1), - enabled = val.enabled ? ''+val.name+'' : val.name; - - markup_list += '
  • \ -
    '+formatNumber(parseFloat(val.ratio).toFixed(2))+'
    \ -
    '+ord+'
    \ -
    '+enabled+'
    \ -
  • '; - }); - - if (e) { - $('.countries_list__footer').fadeOut(); - } else { - $('.countries_list ul').html(''); - $('.countries_list__footer').show(); - - $('.countries_list__header__minioverview').html('Ratio of Loss to Gain'); - } - - $('.countries_list ul').append(markup_list); - - that.model.set('class', 'medium'); - - _.each(data, function(val, key) { - self._drawMiniOverview(val.iso); - }); - }); - } else if (this.model.get('graph') === 'domains') { - var sql = 'SELECT name, total_loss, total_gain, GREATEST(' - - for(var y = 2001; y < 2012; y++) { - sql += 'y'+y+', ' - } - - sql += 'y2012) as max\ - FROM countries_domains\ - ORDER BY total_loss DESC '; - - d3.json('http://wri-01.cartodb.com/api/v2/sql/?q='+encodeURIComponent(sql), function(json) { - var self = that, - markup_list = ''; - - var data = json.rows; - - _.each(data, function(val, key) { - markup_list += ['
  • ', - '
    ', - '
    '+formatNumber(parseFloat(val.total_loss/1000000).toFixed(1))+' Mha
    ', - '
    '+formatNumber(parseFloat(val.total_gain/1000000).toFixed(1))+' Mha
    ', - '
    ', - '
    '+(key+1)+'
    ', - '
    '+val.name+'
    ', - '
  • '].join(''); - }); - - $('.countries_list__footer').hide(); - $('.countries_list__header__minioverview').html('Total loss vs Total gain'); - $('.countries_list ul').html(markup_list); - - that.model.set('class', 'huge'); - - _.each(data, function(val, key) { - self._drawMiniOverview(val.iso); - }); - }); - } - }, - - _toggleClass: function() { - if (this.model.get('class') === 'expanded') { - $('.countries_list__header__minioverview').addClass('expanded'); - $('.countries_list__minioverview').addClass('expanded'); - - $('.countries_list__header__minioverview').removeClass('medium huge'); - $('.countries_list__minioverview').removeClass('medium huge'); - } else if (this.model.get('class') === 'medium') { - $('.countries_list__header__minioverview').addClass('medium'); - $('.countries_list__minioverview').addClass('medium'); - - $('.countries_list__header__minioverview').removeClass('expanded huge'); - $('.countries_list__minioverview').removeClass('expanded huge'); - } else if (this.model.get('class') === 'huge') { - $('.countries_list__header__minioverview').addClass('huge'); - $('.countries_list__minioverview').addClass('huge'); - - $('.countries_list__header__minioverview').removeClass('expanded medium'); - $('.countries_list__minioverview').removeClass('expanded medium'); - } else { - $('.countries_list__header__minioverview').removeClass('expanded medium huge'); - $('.countries_list__minioverview').removeClass('expanded medium huge'); - } - }, - - _drawMiniOverview: function(iso) { - var width = 90, - height = 30; - - var graph = d3.select('.countries_list__minioverview_'+iso) - .append('svg:svg') - .attr('width', width) - .attr('height', height); - - if (this.model.get('graph') === ('total_loss')) { - var sql = 'SELECT '; - - for(var y = 2001; y < 2012; y++) { - sql += 'y'+y+', ' - } - - sql += "y2012, (SELECT y2001_y2012\ - FROM countries_gain\ - WHERE c.iso = iso) as gain\ - FROM loss_gt_0 c \ - WHERE iso = '"+iso+"'"; - - d3.json('https://wri-01.cartodb.com/api/v2/sql?q='+sql, function(json) { - var data = json.rows[0]; - - var data_ = [], - gain = null; - - _.each(data, function(val, key) { - if (key === 'gain') { - gain = val/12; - } else { - data_.push({ - 'year': key.replace('y',''), - 'value': val - }); - } - }); - - var y_scale = d3.scale.linear() - .domain([0, d3.max(data_, function(d) { return d.value; })]) - .range([height, 0]); - - var barWidth = width / data_.length; - - var bar = graph.selectAll('g') - .data(data_) - .enter().append('g') - .attr('transform', function(d, i) { return 'translate(' + (i * barWidth) + ', 0)'; }); - - bar.append('svg:rect') - .attr('class', 'bar') - .attr('y', function(d) { return y_scale(d.value); }) - .attr('height', function(d) { return height - y_scale(d.value); }) - .attr('width', barWidth - 1); - - var data_gain_ = [ - { - year: 2001, - value: gain - }, - { - year: 2012, - value: gain - } - ]; - - graph.selectAll('line.minioverview_line') - .data(data_gain_) - .enter() - .append('line') - .attr({ - 'class': 'minioverview_line', - 'x1': 0, - 'x2': width, - 'y1': function(d) { return y_scale(gain); }, - 'y2': function(d) { return y_scale(gain); } - }); - }); - } else if (this.model.get('graph') === ('percent_loss')) { - var sql = 'WITH loss as (SELECT '; - - for(var y = 2001; y < 2012; y++) { - sql += 'y'+y+' as loss_y'+y+', '; - } - - sql += "y2012 as loss_y2012\ - FROM loss_gt_25\ - WHERE iso = '"+iso+"'), extent as (SELECT "; - - for(var y = 2001; y < 2012; y++) { - sql += 'y'+y+' as extent_y'+y+', '; - } - - sql += "y2012 as extent_y2012\ - FROM extent_gt_25\ - WHERE iso = '"+iso+"')"; - - sql += 'SELECT '; - - for(var y = 2001; y < 2012; y++) { - sql += 'loss_y'+y+'/extent_y'+y+' as percent_'+y+', '; - } - - sql += 'loss_y2012/extent_y2012 as percent_2012\ - FROM loss, extent'; - - d3.json('https://wri-01.cartodb.com/api/v2/sql?q='+encodeURIComponent(sql), function(json) { - var data = json.rows[0]; - - var data_ = []; - - _.each(data, function(val, key) { - data_.push({ - 'year': key.replace('y',''), - 'value': val*100 - }); - }); - - var y_scale = d3.scale.linear() - .domain([0, d3.max(data_, function(d) { return d.value; })]) - .range([height, 0]); - - var barWidth = width / data_.length; - - var bar = graph.selectAll('g') - .data(data_) - .enter().append('g') - .attr('transform', function(d, i) { return 'translate(' + (i * barWidth) + ', 0)'; }); - - bar.append('svg:rect') - .attr('class', 'bar') - .attr('y', function(d) { return y_scale(d.value); }) - .attr('height', function(d) { return height - y_scale(d.value); }) - .attr('width', barWidth - 1); - - }); - } else if (this.model.get('graph') === ('total_extent')) { - var sql = 'SELECT '; - - for(var y = 2001; y < 2012; y++) { - sql += 'loss.y'+y+' as loss_y'+y+', '; - } - - sql += 'loss.y2012 as loss_y2012, '; - - for(var y = 2001; y < 2012; y++) { - sql += 'extent.y'+y+' as extent_y'+y+', '; - } - - sql += "extent.y2012 as extent_y2012\ - FROM loss_gt_0 loss, extent_gt_25 extent\ - WHERE loss.iso = extent.iso\ - AND loss.iso = '"+iso+"'"; - - d3.json('https://wri-01.cartodb.com/api/v2/sql?q='+sql, function(json) { - - var graph2 = d3.select('.countries_list__minioverview_'+iso) - .append('div') - .attr('class', 'sibling') - .append('svg:svg') - .attr('width', width) - .attr('height', height); - - var data = json.rows[0]; - - var data_loss_ = [], - data_extent_ = []; - - _.each(data, function(val, key) { - if (key.indexOf('loss_y') != -1) { - data_loss_.push({ - 'year': key.split('_y')[1], - 'value': val - }); - } - - if (key.indexOf('extent_y') != -1) { - data_extent_.push({ - 'year': key.split('extent_y')[1], - 'value': val - }); - } - }); - - var y_scale_loss = d3.scale.linear() - .domain([0, d3.max(data_loss_, function(d) { return d.value; })]) - .range([height, 0]); - - var y_scale_extent = d3.scale.linear() - .domain([0, d3.max(data_extent_, function(d) { return d.value; })]) - .range([height, 0]); - - var barWidth_loss = width / data_loss_.length; - - var bar = graph.selectAll('g') - .data(data_loss_) - .enter() - .append('g') - .attr('transform', function(d, i) { return 'translate(' + (i * barWidth_loss) + ', 0)'; }); - - bar.append('svg:rect') - .attr('class', 'bar') - .attr('y', function(d) { return y_scale_loss(d.value); }) - .attr('height', function(d) { return height - y_scale_loss(d.value); }) - .attr('width', barWidth_loss - 1); - - var barWidth_extent = width / data_extent_.length; - - var bar2 = graph2.selectAll('g') - .data(data_extent_) - .enter() - .append('g') - .attr('transform', function(d, i) { return 'translate(' + (i * barWidth_extent) + ', 0)'; }); - - bar2.append('svg:rect') - .attr('class', 'bar extent') - .attr('y', function(d) { return y_scale_extent(d.value); }) - .attr('height', function(d) { return height - y_scale_extent(d.value); }) - .attr('width', barWidth_extent - 1); - }); - } - }, - - _drawYears: function() { - var markup_years = ''; - - for (var y = 2001; y<=2012; y += 1) { - var y_ = this.x_scale(y); - - if (y === 2001) { - y_ -= 25; - } else if (y === 2012) { - y_ -= 55; - } else { - y_ -= 40; - } - - markup_years += ''+y+''; - } - - this.$years.html(markup_years); - }, - - _drawGraph: function() { - var that = this; - - var w = this.w, - h = this.h, - vertical_m = this.vertical_m, - m = this.m, - x_scale = this.x_scale; - - var grid_scale = d3.scale.linear() - .range([vertical_m, h-vertical_m]) - .domain([1, 0]); - - d3.select('#chart').remove(); - - var svg = d3.select('.overview_graph__area') - .append('svg:svg') - .attr('id', 'chart') - .attr('width', w) - .attr('height', h); - - // grid - svg.selectAll('line.grid_h') - .data(grid_scale.ticks(4)) - .enter() - .append('line') - .attr({ - 'class': 'grid grid_h', - 'x1': 0, - 'x2': w, - 'y1': function(d, i) { return grid_scale(d); }, - 'y2': function(d, i) { return grid_scale(d); } - }); - - svg.selectAll('line.grid_v') - .data(x_scale.ticks(12)) - .enter() - .append('line') - .attr({ - 'class': 'grid grid_v', - 'y1': h, - 'y2': 0, - 'x1': function(d) { return x_scale(d); }, - 'x2': function(d) { return x_scale(d); } - }); - - var gradient = svg.append('svg:defs') - .append('svg:linearGradient') - .attr('id', 'gradient') - .attr('x1', '0%') - .attr('y1', '0%') - .attr('x2', '0%') - .attr('y2', '100%') - .attr('spreadMethod', 'pad'); - - gradient.append('svg:stop') - .attr('offset', '0%') - .attr('stop-color', '#CA46FF') - .attr('stop-opacity', .5); - - gradient.append('svg:stop') - .attr('offset', '100%') - .attr('stop-color', '#D24DFF') - .attr('stop-opacity', 1); - - if (this.model.get('graph') === 'total_loss') { - this._showYears(); - - svg.append('text') - .attr('class', 'axis') - .attr('id', 'axis_y') - .text('Mha') - .attr('x', -h/2) - .attr('y', 30) - .attr('transform', 'rotate(-90)'); - - var sql = 'SELECT '; - - for(var y = 2001; y < 2012; y++) { - sql += 'SUM(y'+y+') as y'+y+', ' - } - - sql += 'SUM(y2012) as y2012, (SELECT SUM(y2001_y2012)\ - FROM countries_gain) as gain\ - FROM loss_gt_0'; - - d3.json('https://wri-01.cartodb.com/api/v2/sql?q='+sql, function(error, json) { - var data = json.rows[0]; - - var data_ = [], - gain = null; - - _.each(data, function(val, key) { - if (key === 'gain') { - gain = val/12; - } else { - data_.push({ - 'year': key.replace('y',''), - 'value': val - }); - } - }); - - var y_scale = d3.scale.linear() - .range([vertical_m, h-vertical_m]) - .domain([d3.max(data_, function(d) { return d.value; }), 0]); - - // area - var area = d3.svg.area() - .x(function(d) { return x_scale(d.year); }) - .y0(h) - .y1(function(d) { return y_scale(d.value); }); - - svg.append('path') - .datum(data_) - .attr('class', 'area') - .attr('d', area) - .style('fill', 'url(#gradient)'); - - // circles - svg.selectAll('circle') - .data(data_) - .enter() - .append('svg:circle') - .attr('class', 'linedot') - .attr('cx', function(d) { - return x_scale(d.year); - }) - .attr('cy', function(d){ - return y_scale(d.value); - }) - .attr('r', 6) - .attr('name', function(d) { - return ''+d.year+''+formatNumber(parseFloat(d.value/1000000).toFixed(1))+' Mha'; - }) - .on('mouseover', function(d) { - that.tooltip.html($(this).attr('name')) - .style('visibility', 'visible') - .style('top', $(this).offset().top-100+'px') - .style('left', $(this).offset().left-$('.tooltip').width()/2-4+'px') - .attr('class', 'tooltip'); - - d3.select(this) - .transition() - .duration(100) - .attr('r', 7); - - // TODO: highlighting the legend - }) - .on('mouseout', function(d) { - that.tooltip.style('visibility', 'hidden'); - - d3.select(this) - .transition() - .duration(100) - .attr('r', 6); - - // TODO: highlighting the legend - }); - - var data_gain_ = [ - { - year: 2001, - value: gain - }, - { - year: 2012, - value: gain - } - ]; - - // line - svg.selectAll('line.overview_line') - .data([data_gain_[0]]) - .enter() - .append('line') - .attr({ - 'class': 'overview_line', - 'x1': m, - 'x2': w-m, - 'y1': function(d) { return y_scale(gain); }, - 'y2': function(d) { return y_scale(gain); } - }); - - svg.selectAll('circle.gain') - .data(data_gain_) - .enter() - .append('svg:circle') - .attr('class', 'linedot gain') - .attr('cx', function(d) { - return x_scale(d.year); - }) - .attr('cy', function(d){ - return y_scale(d.value); - }) - .attr('r', 6) - .attr('name', function(d) { - return '2001-2012'+formatNumber(parseFloat(d.value/1000000).toFixed(1))+' Mha'; - }) - .on('mouseover', function(d) { - that.tooltip.html($(this).attr('name')) - .style('visibility', 'visible') - .style('top', $(this).offset().top-100+'px') - .style('left', $(this).offset().left-$('.tooltip').width()/2-4+'px') - .attr('class', 'tooltip gain_tooltip'); - - d3.select(this) - .transition() - .duration(100) - .attr('r', 7); - - // TODO: highlighting the legend - }) - .on('mouseout', function(d) { - that.tooltip.style('visibility', 'hidden'); - - d3.select(this) - .transition() - .duration(100) - .attr('r', 6); - - // TODO: highlighting the legend - }); - }); - } else if (this.model.get('graph') === 'percent_loss') { - this._showYears(); - - svg.append('text') - .attr('class', 'axis') - .attr('id', 'axis_y') - .text('%') - .attr('x', -h/2) - .attr('y', 30) - .attr('transform', 'rotate(-90)'); - - var sql = 'WITH loss as (SELECT '; - - for(var y = 2001; y < 2012; y++) { - sql += 'SUM(y'+y+') as sum_loss_y'+y+', '; - } - - sql += 'SUM(y2012) as sum_loss_y2012\ - FROM loss_gt_25), extent as (SELECT '; - - for(var y = 2001; y < 2012; y++) { - sql += 'SUM(y'+y+') as sum_extent_y'+y+', '; - } - - sql += 'SUM(y2012) as sum_extent_y2012\ - FROM extent_gt_25)\ - SELECT '; - - for(var y = 2001; y < 2012; y++) { - sql += 'sum_loss_y'+y+'/sum_extent_y'+y+' as percent_loss_'+y+', '; - } - - sql += 'sum_loss_y2012/sum_extent_y2012 as percent_loss_2012, (SELECT SUM(y2001_y2012)/('; - - for(var y = 2001; y < 2012; y++) { - sql += 'sum_extent_y'+y+' + '; - } - - sql += 'sum_extent_y2012)\ - FROM countries_gain) as gain\ - FROM loss, extent'; - - d3.json('https://wri-01.cartodb.com/api/v2/sql?q='+encodeURIComponent(sql), function(json) { - var data = json.rows[0]; - - var data_ = [], - gain = null; - - _.each(data, function(val, key) { - if (key === 'gain') { - gain = val/12; - } else { - data_.push({ - 'year': key.replace('percent_loss_',''), - 'value': val - }); - } - }); - - var y_scale = grid_scale; - - // area - var area = d3.svg.area() - .x(function(d) { return x_scale(d.year); }) - .y0(h) - .y1(function(d) { return y_scale(d.value*100); }); - - svg.append('path') - .datum(data_) - .attr('class', 'area') - .attr('d', area) - .style('fill', 'url(#gradient)'); - - // circles - svg.selectAll('circle') - .data(data_) - .enter() - .append('svg:circle') - .attr('class', 'linedot') - .attr('cx', function(d) { - return x_scale(d.year); - }) - .attr('cy', function(d){ - return y_scale(d.value*100); - }) - .attr('r', 6) - .attr('name', function(d) { - return ''+d.year+''+parseFloat(d.value*100).toFixed(2)+' %'; - }) - .on('mouseover', function(d) { - that.tooltip.html($(this).attr('name')) - .style('visibility', 'visible') - .style('top', $(this).offset().top-100+'px') - .style('left', $(this).offset().left-$('.tooltip').width()/2-4+'px') - .attr('class', 'tooltip'); - - d3.select(this) - .transition() - .duration(100) - .attr('r', 7); - - // TODO: highlighting the legend - }) - .on('mouseout', function(d) { - that.tooltip.style('visibility', 'hidden'); - - d3.select(this) - .transition() - .duration(100) - .attr('r', 6); - - // TODO: highlighting the legend - }); - - var data_gain_ = [ - { - year: 2001, - value: gain - }, - { - year: 2012, - value: gain - } - ]; - - // line - svg.selectAll('line.overview_line') - .data([data_gain_[0]]) - .enter() - .append('line') - .attr({ - 'class': 'overview_line', - 'x1': m, - 'x2': w-m, - 'y1': function(d) { return y_scale(gain*100); }, - 'y2': function(d) { return y_scale(gain*100); } - }); - - // circles - svg.selectAll('circle.gain') - .data(data_gain_) - .enter() - .append('svg:circle') - .attr('class', 'linedot gain') - .attr('cx', function(d) { - return x_scale(d.year); - }) - .attr('cy', function(d){ - return y_scale(d.value*100); - }) - .attr('r', 6) - .attr('name', function(d) { - return '2001-2012'+parseFloat(d.value*100).toFixed(2)+' %'; - }) - .on('mouseover', function(d) { - that.tooltip.html($(this).attr('name')) - .style('visibility', 'visible') - .style('top', $(this).offset().top-100+'px') - .style('left', $(this).offset().left-$('.tooltip').width()/2-4+'px') - .attr('class', 'tooltip gain_tooltip'); - - d3.select(this) - .transition() - .duration(100) - .attr('r', 7); - - // TODO: highlighting the legend - }) - .on('mouseout', function(d) { - that.tooltip.style('visibility', 'hidden'); - - d3.select(this) - .transition() - .duration(100) - .attr('r', 6); - - // TODO: highlighting the legend - }); - }); - } else if (this.model.get('graph') === 'total_extent') { - this._showYears(); - - svg.append('text') - .attr('class', 'axis') - .attr('id', 'axis_y') - .text('Mha') - .attr('x', -h/2) - .attr('y', 30) - .attr('transform', 'rotate(-90)'); - - var gradient_extent = svg.append('svg:defs') - .append('svg:linearGradient') - .attr('id', 'gradient_extent') - .attr('x1', '0%') - .attr('y1', '0%') - .attr('x2', '0%') - .attr('y2', '100%') - .attr('spreadMethod', 'pad'); - - gradient_extent.append('svg:stop') - .attr('offset', '0%') - .attr('stop-color', '#98BD17') - .attr('stop-opacity', .5); - - gradient_extent.append('svg:stop') - .attr('offset', '100%') - .attr('stop-color', '#98BD17') - .attr('stop-opacity', 1); - - var sql = 'SELECT '; - - for(var y = 2001; y < 2012; y++) { - sql += 'SUM(loss.y'+y+') as loss_y'+y+', '; - } - - sql += 'SUM(loss.y2012) as loss_y2012, '; - - for(var y = 2001; y < 2012; y++) { - sql += 'SUM(extent.y'+y+') as extent_y'+y+', '; - } - - sql += 'SUM(extent.y2012) as extent_y2012\ - FROM loss_gt_25 loss, extent_gt_25 extent\ - WHERE loss.iso = extent.iso'; - - d3.json('https://wri-01.cartodb.com/api/v2/sql?q='+encodeURIComponent(sql), function(json) { - var data = json.rows[0]; - - var data_ = [], - data_loss_ = [], - data_extent_ = []; - - _.each(data, function(val, key) { - var year = key.split('_y')[1]; - - var obj = _.find(data_, function(obj) { return obj.year == year; }); - - if (obj === undefined) { - data_.push({ 'year': year }); - } - - if (key.indexOf('loss_y') != -1) { - data_loss_.push({ - 'year': key.split('_y')[1], - 'value': val - }); - } - - if (key.indexOf('extent_y') != -1) { - data_extent_.push({ - 'year': key.split('extent_y')[1], - 'value': val - }); - } - }); - - _.each(data_, function(val) { - var loss = _.find(data_loss_, function(obj) { return obj.year == val.year; }), - extent = _.find(data_extent_, function(obj) { return obj.year == val.year; }); - - _.extend(val, { 'loss': loss.value, 'extent': extent.value }); - }); - - var domain = [d3.max(data_, function(d) { return d.extent; }), 0]; - - var y_scale = d3.scale.linear() - .range([vertical_m, h-vertical_m]) - .domain(domain); - - // area - var area_loss = d3.svg.area() - .x(function(d) { return x_scale(d.year); }) - .y0(h) - .y1(function(d) { return y_scale(d.loss); }); - - var area_extent = d3.svg.area() - .x(function(d) { return x_scale(d.year); }) - .y0(function(d) { return y_scale(d.extent); }) - .y1(function(d) { return y_scale(d.loss); }); - - svg.append('path') - .datum(data_) - .attr('class', 'area') - .attr('d', area_loss) - .style('fill', 'url(#gradient)'); - - svg.append('path') - .datum(data_) - .attr('class', 'area') - .attr('d', area_extent) - .style('fill', 'url(#gradient_extent)'); - - // circles - svg.selectAll('circle') - .data(data_loss_) - .enter() - .append('svg:circle') - .attr('class', 'linedot') - .attr('cx', function(d) { - return x_scale(d.year); - }) - .attr('cy', function(d){ - return y_scale(d.value); - }) - .attr('r', 6) - .attr('name', function(d) { - return ''+d.year+''+formatNumber(parseFloat(d.value/1000000).toFixed(1))+' Mha'; - }) - .on('mouseover', function(d) { - that.tooltip.html($(this).attr('name')) - .style('visibility', 'visible') - .style('top', $(this).offset().top-100+'px') - .style('left', $(this).offset().left-$('.tooltip').width()/2-4+'px') - .attr('class', 'tooltip'); - - d3.select(this) - .transition() - .duration(100) - .attr('r', 7); - - // TODO: highlighting the legend - }) - .on('mouseout', function(d) { - that.tooltip.style('visibility', 'hidden'); - - d3.select(this) - .transition() - .duration(100) - .attr('r', 6); - - // TODO: highlighting the legend - }); - - svg.selectAll('circle.gain') - .data(data_extent_) - .enter() - .append('svg:circle') - .attr('class', 'linedot gain') - .attr('cx', function(d) { - return x_scale(d.year); - }) - .attr('cy', function(d){ - return y_scale(d.value); - }) - .attr('r', 6) - .attr('name', function(d) { - return ''+d.year+''+formatNumber(parseFloat(d.value/1000000).toFixed(1))+' Mha'; - }) - .on('mouseover', function(d) { - that.tooltip.html($(this).attr('name')) - .style('visibility', 'visible') - .style('top', $(this).offset().top-100+'px') - .style('left', $(this).offset().left-$('.tooltip').width()/2-4+'px') - .attr('class', 'tooltip gain_tooltip'); - - d3.select(this) - .transition() - .duration(100) - .attr('r', 7); - - // TODO: highlighting the legend - }) - .on('mouseout', function(d) { - that.tooltip.style('visibility', 'hidden'); - - d3.select(this) - .transition() - .duration(100) - .attr('r', 6); - - // TODO: highlighting the legend - }); - }); - } else if (this.model.get('graph') === 'ratio') { - this._hideYears(); - - svg.append('text') - .attr('class', 'axis light') - .attr('id', 'axis_y') - .text('Ratio of tree cover loss to gain 2001-2012') - .attr('x', -(h/2)-60) - .attr('y', 30) - .attr('transform', 'rotate(-90)'); - - svg.append('text') - .attr('class', 'axis light') - .attr('id', 'axis_ratio') - .text('1') - .attr('x', 25) - .attr('y', h-60); - - var shadow = svg.append('svg:defs') - .append('svg:filter') - .attr('id', 'shadow') - .attr('x', '0%') - .attr('y', '0%') - .attr('width', '200%') - .attr('height', '200%'); - - shadow.append('svg:feOffset') - .attr('result', 'offOut') - .attr('in', 'SourceAlpha') - .attr('dx', 0) - .attr('dy', 0); - - shadow.append('svg:feGaussianBlur') - .attr('result', 'blurOut') - .attr('in', 'offOut') - .attr('stdDeviation', 1); - - shadow.append('svg:feBlend') - .attr('in', 'SourceGraphic') - .attr('in2', 'blurOut') - .attr('mode', 'normal'); - - var sql = 'WITH loss as (SELECT iso, SUM('; - - for(var y = 2001; y < 2012; y++) { - sql += 'loss.y'+y+' + '; - } - - sql += ['loss.y2012) as sum_loss', - 'FROM loss_gt_50 loss', - 'GROUP BY iso), gain as ('].join(' '); - - sql += ['SELECT g.iso, SUM(y2001_y2012) as sum_gain', - 'FROM countries_gain g, loss_gt_50 loss', - 'WHERE loss.iso = g.iso', - 'GROUP BY g.iso), ratio as ('].join(' '); - - sql += ['SELECT c.iso, c.name, c.enabled, loss.sum_loss as loss,', - 'gain.sum_gain as gain, loss.sum_loss/gain.sum_gain as ratio', - 'FROM loss, gain, gfw2_countries c', - 'WHERE sum_gain IS NOT null', - 'AND NOT sum_gain = 0', - 'AND c.iso = gain.iso', - 'AND c.iso = loss.iso', - 'ORDER BY loss.sum_loss DESC', - 'LIMIT 50), extent as ('].join(' '); - - sql += ['SELECT extent.iso, SUM(extent.y2012) as extent', - 'FROM countries_extent extent', - 'GROUP BY extent.iso) '].join(' '); - - sql += ['SELECT *', - 'FROM ratio, extent', - 'WHERE ratio IS NOT null', - 'AND extent.iso = ratio.iso'].join(' '); - - d3.json('https://wri-01.cartodb.com/api/v2/sql?q='+encodeURIComponent(sql), function(json) { - var data = json.rows; - - var log_m = 50; - - var x_log_scale = d3.scale.log() - .range([m, w-m]) - .domain([d3.min(data, function(d) { return d.extent; }), d3.max(data, function(d) { return d.extent; })]); - - var y_log_scale = d3.scale.log() - .range([h-log_m, m]) - .domain([d3.min(data, function(d) { return d.ratio; }), d3.max(data, function(d) { return d.ratio; })]); - - var color_scale = d3.scale.linear() - .domain([d3.min(data, function(d) { return d.ratio; }), 1, 10, d3.max(data, function(d) { return d.ratio; })]) - .range(["#9ABF00", "#9ABF00", "#CA46FF", "#CA46FF"]); - - // line - svg.selectAll('line.linear_regression') - .data([1]) - .enter() - .append('line') - .attr({ - 'class': 'linear_regression', - 'x1': m, - 'x2': w-m, - 'y1': function(d) { return y_log_scale(d); }, - 'y2': function(d) { return y_log_scale(d); }, - "stroke-width": 1.3, - "stroke": "white", - "stroke-dasharray": "7,5" - }); - - // circles w/ magic numbers :( - var circle_attr = { - 'cx': function(d) { return x_log_scale(d.extent) }, - 'cy': function(d) { return y_log_scale(d.ratio) }, - 'r': 5, - 'name': function(d) { return d.name; }, - 'class': function(d) { return d.enabled ? 'ball ball_link' : 'ball ball_nolink'; } - }; - - var data_ = [], - data_link_ = [], - exclude = []; - - _.each(data, function(row) { - if (!_.contains(exclude, row.name)) { - if (row.enabled === true) { - data_link_.push(row); - } else { - data_.push(row); - } - } - }); - - var circles_link = svg.selectAll('circle.ball_link') - .data(data_link_) - .enter() - .append('a') - .attr('xlink:href', function(d) { return '/country/' + d.iso}) - .append('svg:circle') - .attr(circle_attr) - .style('fill', function(d) { - return color_scale(d.ratio); - }) - .style('filter', 'url(#shadow)') - .on('mouseover', function() { - d3.select(d3.event.target) - .transition() - .attr('r', 7) - .style('opacity', 1); - - var t = $(this).offset().top - 80, - l = $(this).offset().left, - r = $(this).attr('r'), - tip = $('.tooltip').width()/2; - - that.tooltip.html($(this).attr('name')) - .style('visibility', 'visible') - .style('top', parseInt(t, 10)+'px') - .style('left', parseInt(l, 10)+parseInt(r, 10)-parseInt(tip, 10)-10+'px') - .attr('class', 'tooltip gain_tooltip'); - }) - .on('mouseenter', function() { - d3.select(d3.event.target) - .transition() - .attr('r', 7) - .style('opacity', 1); - - var t = $(this).offset().top - 80, - l = $(this).offset().left, - r = $(this).attr('r'), - tip = $('.tooltip').width()/2; - - that.tooltip.html($(this).attr('name')) - .style('visibility', 'visible') - .style('top', parseInt(t, 10)+'px') - .style('left', parseInt(l, 10)+parseInt(r, 10)-parseInt(tip, 10)+'px') - .attr('class', 'tooltip gain_tooltip'); - }) - .on('mouseout', function() { - d3.select(d3.event.target) - .transition() - .attr('r', 5) - .style('opacity', .8); - - that.tooltip.style('visibility', 'hidden'); - }); - - var circles = svg.selectAll('circle.ball_nolink') - .data(data_) - .enter() - .append('svg:circle') - .attr(circle_attr) - .style('fill', function(d) { - return color_scale(d.ratio); - }) - .style('filter', 'url(#shadow)') - .on('mouseover', function() { - d3.select(d3.event.target) - .transition() - .attr('r', 7) - .style('opacity', 1); - - var t = $(this).offset().top - 80, - l = $(this).offset().left, - r = $(this).attr('r'), - tip = $('.tooltip').width()/2; - - that.tooltip.html($(this).attr('name')) - .style('visibility', 'visible') - .style('top', parseInt(t, 10)+'px') - .style('left', parseInt(l, 10)+parseInt(r, 10)-parseInt(tip, 10)-10+'px') - .attr('class', 'tooltip gain_tooltip'); - }) - .on('mouseenter', function() { - d3.select(d3.event.target) - .transition() - .attr('r', 7) - .style('opacity', 1); - - var t = $(this).offset().top - 80, - l = $(this).offset().left, - r = $(this).attr('r'), - tip = $('.tooltip').width()/2; - - that.tooltip.html($(this).attr('name')) - .style('visibility', 'visible') - .style('top', parseInt(t, 10)+'px') - .style('left', parseInt(l, 10)+parseInt(r, 10)-parseInt(tip, 10)+'px') - .attr('class', 'tooltip gain_tooltip'); - }) - .on('mouseout', function() { - d3.select(d3.event.target) - .transition() - .attr('r', 5) - .style('opacity', .8); - - that.tooltip.style('visibility', 'hidden'); - }); - }); - } else if (this.model.get('graph') === 'domains') { - this._showYears(); - - var sql = 'SELECT name, '; - - for(var y = 2001; y < 2012; y++) { - sql += 'y'+y+', ' - } - - sql += 'y2012, GREATEST(' - - for(var y = 2001; y < 2012; y++) { - sql += 'y'+y+', ' - } - - sql += 'y2012) as max\ - FROM countries_domains'; - - d3.json('https://wri-01.cartodb.com/api/v2/sql?q='+sql, function(error, json) { - var data = json.rows; - - var r_scale = d3.scale.linear() - .range([5, 30]) // max ball radius - .domain([0, d3.max(data, function(d) { return d.max; })]) - - for(var j = 0; j < data.length; j++) { - var data_ = [], - domain = ''; - - _.each(data[j], function(val, key) { - if (key !== 'max') { - if (key === 'name') { - domain = val.toLowerCase(); - } else { - data_.push({ - 'year': key.replace('y',''), - 'value': val - }); - } - } - }); - - svg.append('text') - .attr('class', 'label') - .attr('id', 'label_'+domain) - .text(domain) - .attr('x', function() { - var l = x_scale(2002) - $(this).width()/2; - - return l; - }) - .attr('y', (h/5)*(j+.6)); - - var circle_attr = { - 'cx': function(d, i) { return x_scale(2001 + i); }, - 'cy': function(d) { return (h/5)*(j+1); }, - 'r': function(d) { return r_scale(d.value); }, - 'class': function(d) { return 'ball'; } - }; - - svg.selectAll('circle.domain_'+domain) - .data(data_) - .enter() - .append('svg:circle') - .attr(circle_attr) - .attr('data-slug', domain) - .attr('name', function(d) { - return ''+d.year+''+formatNumber(parseFloat(d.value/1000000).toFixed(1))+' Mha'; - }) - .style('fill', function(d) { return config.GRAPHCOLORS[domain]; }) - .on('mouseover', function() { - d3.select(d3.event.target) - .transition() - .attr('r', function(d) { return circle_attr.r(d) + 2; }) - .style('opacity', 1); - - var t = $(this).offset().top - 100, - l = $(this).offset().left, - r = $(this).attr('r'), - tip = $('.tooltip').width()/2, - slug = $(this).attr('data-slug'); - - that.tooltip.html($(this).attr('name')) - .style('visibility', 'visible') - .style('top', parseInt(t, 10)+'px') - .style('left', parseInt(l, 10)+parseInt(r, 10)-parseInt(tip, 10)-10+'px') - .attr('class', 'tooltip') - .attr('data-slug', 'tooltip') - .style('color', function() { - if (slug === 'subtropical') { - return '#FFC926' - } else { - return config.GRAPHCOLORS[slug]; - } - }); - }) - .on('mouseenter', function() { - d3.select(d3.event.target) - .transition() - .attr('r', function(d) { return circle_attr.r(d) + 2; }) - .style('opacity', 1); - - var t = $(this).offset().top - 80, - l = $(this).offset().left, - r = $(this).attr('r'), - tip = $('.tooltip').width()/2, - slug = $(this).attr('data-slug'); - - that.tooltip.html($(this).attr('name')) - .style('visibility', 'visible') - .style('top', parseInt(t, 10)+'px') - .style('left', parseInt(l, 10)+parseInt(r, 10)-parseInt(tip, 10)-10+'px') - .attr('class', 'tooltip') - .attr('data-slug', 'tooltip') - .style('color', function() { - if (domain === 'subtropical') { return config.GRAPHCOLORS[domain]; } - }); - }) - .on('mouseout', function() { - d3.select(d3.event.target) - .transition() - .attr('r', function(d) { return circle_attr.r(d); }) - .style('opacity', .8); - - that.tooltip - .style('color', '') - .style('visibility', 'hidden'); - }); - } - }); - } - } -}); - - -gfw.ui.view.CountriesEmbedShow = cdb.core.View.extend({ - el: document.body, - - events: { - 'click .forma_dropdown-link': '_openDropdown', - 'click .hansen_dropdown-link': '_openDropdown', - 'click .hansen_dropdown-menu a': '_redrawCircle' - }, - - initialize: function() { - this.iso = this.options.iso; - - this._initViews(); - this._initHansenDropdown(); - }, - - _initViews: function() { - this._drawCircle('forma', 'lines', { iso: this.iso }); - this._drawCircle('forest_loss', 'bars', { iso: this.iso, dataset: 'loss' }); - }, - - _initFormaDropdown: function() { - $('.forma_dropdown-link').qtip({ - show: 'click', - hide: { - event: 'click unfocus' - }, - content: { - text: $('.forma_dropdown-menu') - }, - position: { - my: 'bottom right', - at: 'top right', - target: $('.forma_dropdown-link'), - adjust: { - x: -10 - } - }, - style: { - tip: { - corner: 'bottom right', - mimic: 'bottom center', - border: 1, - width: 10, - height: 6 - } - } - }); - }, - - _initHansenDropdown: function() { - this.dropdown = $('.hansen_dropdown-link').qtip({ - show: 'click', - hide: { - event: 'click unfocus' - }, - content: { - text: $('.hansen_dropdown-menu') - }, - position: { - my: 'top right', - at: 'bottom right', - target: $('.hansen_dropdown-link'), - adjust: { - x: 10 - } - }, - style: { - tip: { - corner: 'top right', - mimic: 'top center', - border: 1, - width: 10, - height: 6 - } - } - }); - }, - - _openDropdown: function(e) { - e.preventDefault(); - }, - - _redrawCircle: function(e) { - e.preventDefault(); - - var dataset = $(e.target).attr('data-slug'), - subtitle = $(e.target).text(); - - var api = this.dropdown.qtip('api'); - - api.hide(); - - $('.hansen_dropdown-link').html(subtitle); - - if (dataset === 'countries_gain') { - this._drawCircle('forest_loss', 'comp', { iso: this.iso }); - } else { - this._drawCircle('forest_loss', 'bars', { iso: this.iso, dataset: dataset }); - } - }, - - _drawCircle: function(id, type, options) { - var that = this; - - var $graph = $('.'+id), - $amount = $('.'+id+' .graph-amount'), - $date = $('.'+id+' .graph-date'), - $coming_soon = $('.'+id+' .coming_soon'), - $action = $('.'+id+' .action'); - - $('.graph.'+id+' .frame_bkg').empty(); - $graph.addClass('ghost'); - $amount.html(''); - $date.html(''); - $coming_soon.hide(); - - var width = options.width || 256, - height = options.height || width, - h = 100, // maxHeight - radius = width / 2; - - var graph = d3.select('.graph.'+id+' .frame_bkg') - .append('svg:svg') - .attr('class', type) - .attr('width', width) - .attr('height', height); - - var dashedLines = [ - { x1:17, y:height/4, x2:239, color: '#ccc' }, - { x1:0, y:height/2, x2:width, color: '#ccc' }, - { x1:17, y:3*height/4, x2:239, color: '#ccc' } - ]; - - // Adds the dotted lines - _.each(dashedLines, function(line) { - graph.append('svg:line') - .attr('x1', line.x1) - .attr('y1', line.y) - .attr('x2', line.x2) - .attr('y2', line.y) - .style('stroke-dasharray', '2,2') - .style('stroke', line.color); - }); - - var sql = ["SELECT date_trunc('month', date) as date, COUNT(*) as alerts", - 'FROM forma_api', - "WHERE iso = '"+options.iso+"'", - "GROUP BY date_trunc('month', date)", - "ORDER BY date_trunc('month', date) ASC"].join(' '); - - if (type === 'lines') { - d3.json('https://wri-01.cartodb.com/api/v2/sql?q='+sql, function(json) { - if (json && json.rows.length > 0) { - $graph.removeClass('ghost'); - $action.removeClass('disabled'); - that._initFormaDropdown(); - - var data = json.rows.slice(1, json.rows.length); - } else { - $coming_soon.show(); - - return; - } - - var x_scale = d3.scale.linear() - .domain([0, data.length - 1]) - .range([0, width - 80]); - - var max = d3.max(data, function(d) { return parseFloat(d.alerts); }); - - if (max === d3.min(data, function(d) { return parseFloat(d.alerts); })) { - h = h/2; - } - - var y_scale = d3.scale.linear() - .domain([0, max]) - .range([0, h]); - - var line = d3.svg.line() - .x(function(d, i) { return x_scale(i); }) - .y(function(d, i) { return h - y_scale(d.alerts); }) - .interpolate('basis'); - - var marginLeft = 40, - marginTop = radius - h/2; - - $amount.html(''+formatNumber(data[data.length - 1].alerts)+''); - - var date = new Date(data[data.length - 1].date), - form_date = 'Alerts in ' + config.MONTHNAMES[date.getUTCMonth()] + ' ' + date.getUTCFullYear(); - - $date.html(form_date); - - var cx = width - 80 + marginLeft; - var cy = h - y_scale(data[data.length - 1].alerts) + marginTop; - - graph.append('svg:path') - .attr('transform', 'translate(' + marginLeft + ',' + marginTop + ')') - .attr('d', line(data)) - .on('mousemove', function(d) { - var index = Math.round(x_scale.invert(d3.mouse(this)[0])); - - if (data[index]) { // if there's data - $amount.html(''+formatNumber(data[index].alerts)+''); - - var date = new Date(data[index].date), - form_date = 'Alerts in ' + config.MONTHNAMES[date.getUTCMonth()] + ' ' + date.getUTCFullYear(); - - $date.html(form_date); - - var cx = d3.mouse(this)[0] + marginLeft; - var cy = h - y_scale(data[index].alerts) + marginTop; - - graph.select('.forma_marker') - .attr('cx', cx) - .attr('cy', cy); - } - }); - - graph.append('svg:circle') - .attr('class', 'forma_marker') - .attr('cx', cx) - .attr('cy', cy) - .attr('r', 5); - }); - } else if (type === 'bars') { - var sql = "SELECT "; - - if (options.dataset === 'loss') { - sql += "year, loss_gt_0 loss FROM umd WHERE iso='"+options.iso+"'"; - } else if (options.dataset === 'extent') { - sql += "year, extent_gt_25 extent FROM umd WHERE iso='"+options.iso+"'"; - } - - d3.json('https://wri-01.cartodb.com/api/v2/sql?q='+sql, function(json) { - if (json) { - $graph.removeClass('ghost'); - - var data = json.rows[0]; - } else { - $coming_soon.show(); - - return; - } - - var data_ = []; - - _.each(data, function(val, key) { - if (val.year >= 2001) { - data_.push({ - 'year': val.year, - 'value': eval('val.'+options.dataset) - }); - } - }); - - $amount.html(''+formatNumber(parseInt(data_[data_.length - 1].value, 10))+''); - $date.html('Hectares in ' + data_[data_.length - 1].year); - - var marginLeft = 40, - marginTop = radius - h/2 + 5; - - var y_scale = d3.scale.linear() - .domain([0, d3.max(data_, function(d) { return parseFloat(d.value); })]) - .range([height, marginTop*2]); - - var barWidth = (width - 80) / data_.length; - - var bar = graph.selectAll('g') - .data(data_) - .enter() - .append('g') - .attr('transform', function(d, i) { return 'translate(' + (marginLeft + i * barWidth) + ','+ -marginTop+')'; }); - - bar.append('svg:rect') - .attr('class', function(d, i) { - if (i === 11) { // last year index - return 'last bar' - } else { - return 'bar' - } - }) - .attr('y', function(d) { return y_scale(d.value); }) - .attr('height', function(d) { return height - y_scale(d.value); }) - .attr('width', barWidth - 1) - .on('mouseover', function(d) { - d3.selectAll('.bar').style('opacity', '.5'); - d3.select(this).style('opacity', '1'); - - $amount.html(''+formatNumber(parseInt(d.value, 10))+''); - $date.html('Hectares in ' + d.year); - }); - }); - } else if (type === 'comp') { - var sql = "SELECT iso, sum(umd.loss_gt_0) loss, max(umd.gain) gain FROM umd WHERE iso='"+options.iso+"'"; - - d3.json('https://wri-01.cartodb.com/api/v2/sql?q='+encodeURIComponent(sql), function(json) { - if (json) { - $graph.removeClass('ghost'); - - var data = json.rows[0]; - } else { - $coming_soon.show(); - - return; - } - - var data_ = [{ - 'key': 'Tree cover gain', - 'value': data.gain - }, { - 'key': 'Tree cover loss', - 'value': data.loss - }]; - - $amount.html(''+formatNumber(parseInt(data_[data_.length - 1].value, 10))+''); - $date.html('Ha '+data_[data_.length - 1].key); - - var barWidth = (width - 80) / 12; - - var marginLeft = 40 + barWidth*5, - marginTop = radius - h/2 + 5; - - var y_scale = d3.scale.linear() - .domain([0, d3.max(data_, function(d) { return parseFloat(d.value); })]) - .range([height, marginTop*2]); - - var bar = graph.selectAll('g') - .data(data_) - .enter() - .append('g') - .attr('transform', function(d, i) { return 'translate(' + (marginLeft + i * barWidth) + ',' + -marginTop + ')'; }); - - bar.append('svg:rect') - .attr('class', function(d, i) { - if (i === 1) { // last bar index - return 'last bar' - } else { - return 'bar' - } - }) - .attr('y', function(d) { return y_scale(d.value); }) - .attr('height', function(d) { return height - y_scale(d.value); }) - .attr('width', barWidth - 1) - .style('fill', '#FFC926') - .style('shape-rendering', 'crispEdges') - .on('mouseover', function(d) { - d3.selectAll('.bar').style('opacity', '.5'); - d3.select(this).style('opacity', '1'); - - $amount.html(''+formatNumber(parseFloat(d.value).toFixed(1))+''); - $date.html('Ha '+d.key); - }); - }); - } - } -}); \ No newline at end of file +//= require_tree ./countries \ No newline at end of file diff --git a/app/assets/javascripts/countries/models/countries_overview.js b/app/assets/javascripts/countries/models/countries_overview.js new file mode 100644 index 0000000000..be1dd54b08 --- /dev/null +++ b/app/assets/javascripts/countries/models/countries_overview.js @@ -0,0 +1,7 @@ +gfw.ui.model.CountriesOverview = cdb.core.Model.extend({ + defaults: { + graph: 'total_loss', + years: true, + class: null + } +}); diff --git a/app/assets/javascripts/countries/models/country.js b/app/assets/javascripts/countries/models/country.js new file mode 100644 index 0000000000..21d5e91982 --- /dev/null +++ b/app/assets/javascripts/countries/models/country.js @@ -0,0 +1,24 @@ +gfw.ui.model.Country = cdb.core.Model.extend({ + + url: function() { + return "http://wri-01.cartodb.com/api/v2/sql?q=SELECT iso,id_1,name_1 FROM gadm2_provinces where iso = '" + this.get('iso') + "' order by id_1 asc"; + }, + + parse: function(response, options) { + var collection = new gfw.ui.collection.CountryAreas() + + _.each(response.rows, function(row) { + collection.add(new gfw.ui.model.CountryArea(row)) + }); + + return {fields: response.fields, areas: collection } + } + +}); + +gfw.ui.model.CountryArea = cdb.core.Model.extend({ +}); + +gfw.ui.collection.CountryAreas = Backbone.Collection.extend({ + model: gfw.ui.model.CountryArea +}); \ No newline at end of file diff --git a/app/assets/javascripts/countries/views/embed.js b/app/assets/javascripts/countries/views/embed.js new file mode 100644 index 0000000000..32749e4d7a --- /dev/null +++ b/app/assets/javascripts/countries/views/embed.js @@ -0,0 +1,358 @@ +gfw.ui.view.CountriesEmbedShow = cdb.core.View.extend({ + el: document.body, + + events: { + 'click .forma_dropdown-link': '_openDropdown', + 'click .hansen_dropdown-link': '_openDropdown', + 'click .hansen_dropdown-menu a': '_redrawCircle' + }, + + initialize: function() { + this.iso = this.options.iso; + + this._initViews(); + this._initHansenDropdown(); + }, + + _initViews: function() { + this._drawCircle('forma', 'lines', { iso: this.iso }); + this._drawCircle('forest_loss', 'bars', { iso: this.iso, dataset: 'loss' }); + }, + + _initFormaDropdown: function() { + $('.forma_dropdown-link').qtip({ + show: 'click', + hide: { + event: 'click unfocus' + }, + content: { + text: $('.forma_dropdown-menu') + }, + position: { + my: 'bottom right', + at: 'top right', + target: $('.forma_dropdown-link'), + adjust: { + x: -10 + } + }, + style: { + tip: { + corner: 'bottom right', + mimic: 'bottom center', + border: 1, + width: 10, + height: 6 + } + } + }); + }, + + _initHansenDropdown: function() { + this.dropdown = $('.hansen_dropdown-link').qtip({ + show: 'click', + hide: { + event: 'click unfocus' + }, + content: { + text: $('.hansen_dropdown-menu') + }, + position: { + my: 'top right', + at: 'bottom right', + target: $('.hansen_dropdown-link'), + adjust: { + x: 10 + } + }, + style: { + tip: { + corner: 'top right', + mimic: 'top center', + border: 1, + width: 10, + height: 6 + } + } + }); + }, + + _openDropdown: function(e) { + e.preventDefault(); + }, + + _redrawCircle: function(e) { + e.preventDefault(); + + var dataset = $(e.target).attr('data-slug'), + subtitle = $(e.target).text(); + + var api = this.dropdown.qtip('api'); + + api.hide(); + + $('.hansen_dropdown-link').html(subtitle); + + if (dataset === 'countries_gain') { + this._drawCircle('forest_loss', 'comp', { iso: this.iso }); + } else { + this._drawCircle('forest_loss', 'bars', { iso: this.iso, dataset: dataset }); + } + }, + + _drawCircle: function(id, type, options) { + var that = this; + + var $graph = $('.'+id), + $amount = $('.'+id+' .graph-amount'), + $date = $('.'+id+' .graph-date'), + $coming_soon = $('.'+id+' .coming_soon'), + $action = $('.'+id+' .action'); + + $('.graph.'+id+' .frame_bkg').empty(); + $graph.addClass('ghost'); + $amount.html(''); + $date.html(''); + $coming_soon.hide(); + + var width = options.width || 256, + height = options.height || width, + h = 100, // maxHeight + radius = width / 2; + + var graph = d3.select('.graph.'+id+' .frame_bkg') + .append('svg:svg') + .attr('class', type) + .attr('width', width) + .attr('height', height); + + var dashedLines = [ + { x1:17, y:height/4, x2:239, color: '#ccc' }, + { x1:0, y:height/2, x2:width, color: '#ccc' }, + { x1:17, y:3*height/4, x2:239, color: '#ccc' } + ]; + + // Adds the dotted lines + _.each(dashedLines, function(line) { + graph.append('svg:line') + .attr('x1', line.x1) + .attr('y1', line.y) + .attr('x2', line.x2) + .attr('y2', line.y) + .style('stroke-dasharray', '2,2') + .style('stroke', line.color); + }); + + var sql = ["SELECT date_trunc('month', date) as date, COUNT(*) as alerts", + 'FROM forma_api', + "WHERE iso = '"+options.iso+"'", + "GROUP BY date_trunc('month', date)", + "ORDER BY date_trunc('month', date) ASC"].join(' '); + + if (type === 'lines') { + d3.json('https://wri-01.cartodb.com/api/v2/sql?q='+sql, function(json) { + if (json && json.rows.length > 0) { + $graph.removeClass('ghost'); + $action.removeClass('disabled'); + that._initFormaDropdown(); + + var data = json.rows.slice(1, json.rows.length); + } else { + $coming_soon.show(); + + return; + } + + var x_scale = d3.scale.linear() + .domain([0, data.length - 1]) + .range([0, width - 80]); + + var max = d3.max(data, function(d) { return parseFloat(d.alerts); }); + + if (max === d3.min(data, function(d) { return parseFloat(d.alerts); })) { + h = h/2; + } + + var y_scale = d3.scale.linear() + .domain([0, max]) + .range([0, h]); + + var line = d3.svg.line() + .x(function(d, i) { return x_scale(i); }) + .y(function(d, i) { return h - y_scale(d.alerts); }) + .interpolate('basis'); + + var marginLeft = 40, + marginTop = radius - h/2; + + $amount.html(''+formatNumber(data[data.length - 1].alerts)+''); + + var date = new Date(data[data.length - 1].date), + form_date = 'Alerts in ' + config.MONTHNAMES[date.getUTCMonth()] + ' ' + date.getUTCFullYear(); + + $date.html(form_date); + + var cx = width - 80 + marginLeft; + var cy = h - y_scale(data[data.length - 1].alerts) + marginTop; + + graph.append('svg:path') + .attr('transform', 'translate(' + marginLeft + ',' + marginTop + ')') + .attr('d', line(data)) + .on('mousemove', function(d) { + var index = Math.round(x_scale.invert(d3.mouse(this)[0])); + + if (data[index]) { // if there's data + $amount.html(''+formatNumber(data[index].alerts)+''); + + var date = new Date(data[index].date), + form_date = 'Alerts in ' + config.MONTHNAMES[date.getUTCMonth()] + ' ' + date.getUTCFullYear(); + + $date.html(form_date); + + var cx = d3.mouse(this)[0] + marginLeft; + var cy = h - y_scale(data[index].alerts) + marginTop; + + graph.select('.forma_marker') + .attr('cx', cx) + .attr('cy', cy); + } + }); + + graph.append('svg:circle') + .attr('class', 'forma_marker') + .attr('cx', cx) + .attr('cy', cy) + .attr('r', 5); + }); + } else if (type === 'bars') { + var sql = "SELECT "; + + if (options.dataset === 'loss') { + sql += "year, loss_gt_0 loss FROM umd WHERE iso='"+options.iso+"'"; + } else if (options.dataset === 'extent') { + sql += "year, extent_gt_25 extent FROM umd WHERE iso='"+options.iso+"'"; + } + + d3.json('https://wri-01.cartodb.com/api/v2/sql?q='+sql, function(json) { + if (json) { + $graph.removeClass('ghost'); + + var data = json.rows[0]; + } else { + $coming_soon.show(); + + return; + } + + var data_ = []; + + _.each(data, function(val, key) { + if (val.year >= 2001) { + data_.push({ + 'year': val.year, + 'value': eval('val.'+options.dataset) + }); + } + }); + + $amount.html(''+formatNumber(parseInt(data_[data_.length - 1].value, 10))+''); + $date.html('Hectares in ' + data_[data_.length - 1].year); + + var marginLeft = 40, + marginTop = radius - h/2 + 5; + + var y_scale = d3.scale.linear() + .domain([0, d3.max(data_, function(d) { return parseFloat(d.value); })]) + .range([height, marginTop*2]); + + var barWidth = (width - 80) / data_.length; + + var bar = graph.selectAll('g') + .data(data_) + .enter() + .append('g') + .attr('transform', function(d, i) { return 'translate(' + (marginLeft + i * barWidth) + ','+ -marginTop+')'; }); + + bar.append('svg:rect') + .attr('class', function(d, i) { + if (i === 11) { // last year index + return 'last bar' + } else { + return 'bar' + } + }) + .attr('y', function(d) { return y_scale(d.value); }) + .attr('height', function(d) { return height - y_scale(d.value); }) + .attr('width', barWidth - 1) + .on('mouseover', function(d) { + d3.selectAll('.bar').style('opacity', '.5'); + d3.select(this).style('opacity', '1'); + + $amount.html(''+formatNumber(parseInt(d.value, 10))+''); + $date.html('Hectares in ' + d.year); + }); + }); + } else if (type === 'comp') { + var sql = "SELECT iso, sum(umd.loss_gt_0) loss, max(umd.gain) gain FROM umd WHERE iso='"+options.iso+"'"; + + d3.json('https://wri-01.cartodb.com/api/v2/sql?q='+encodeURIComponent(sql), function(json) { + if (json) { + $graph.removeClass('ghost'); + + var data = json.rows[0]; + } else { + $coming_soon.show(); + + return; + } + + var data_ = [{ + 'key': 'Tree cover gain', + 'value': data.gain + }, { + 'key': 'Tree cover loss', + 'value': data.loss + }]; + + $amount.html(''+formatNumber(parseInt(data_[data_.length - 1].value, 10))+''); + $date.html('Ha '+data_[data_.length - 1].key); + + var barWidth = (width - 80) / 12; + + var marginLeft = 40 + barWidth*5, + marginTop = radius - h/2 + 5; + + var y_scale = d3.scale.linear() + .domain([0, d3.max(data_, function(d) { return parseFloat(d.value); })]) + .range([height, marginTop*2]); + + var bar = graph.selectAll('g') + .data(data_) + .enter() + .append('g') + .attr('transform', function(d, i) { return 'translate(' + (marginLeft + i * barWidth) + ',' + -marginTop + ')'; }); + + bar.append('svg:rect') + .attr('class', function(d, i) { + if (i === 1) { // last bar index + return 'last bar' + } else { + return 'bar' + } + }) + .attr('y', function(d) { return y_scale(d.value); }) + .attr('height', function(d) { return height - y_scale(d.value); }) + .attr('width', barWidth - 1) + .style('fill', '#FFC926') + .style('shape-rendering', 'crispEdges') + .on('mouseover', function(d) { + d3.selectAll('.bar').style('opacity', '.5'); + d3.select(this).style('opacity', '1'); + + $amount.html(''+formatNumber(parseFloat(d.value).toFixed(1))+''); + $date.html('Ha '+d.key); + }); + }); + } + } +}); \ No newline at end of file diff --git a/app/assets/javascripts/countries/views/index.js b/app/assets/javascripts/countries/views/index.js new file mode 100644 index 0000000000..767ee49745 --- /dev/null +++ b/app/assets/javascripts/countries/views/index.js @@ -0,0 +1,37 @@ +gfw.ui.view.CountriesIndex = cdb.core.View.extend({ + el: document.body, + + initialize: function() { + this._drawCountries(); + }, + + _drawCountries: function() { + var that = this; + + var sql = ['SELECT c.iso, c.enabled, m.the_geom', + 'FROM ne_50m_admin_0_countries m, gfw2_countries c', + 'WHERE c.iso = m.adm0_a3 AND c.enabled', + '&format=topojson'].join(' '); + + var sql_ = ['SELECT c.iso, m.the_geom', + 'FROM ne_50m_admin_0_countries m, gfw2_countries c', + 'WHERE c.iso = m.adm0_a3', + "AND c.iso = 'TWN'&format=topojson"].join(' '); + + d3.json('https://wri-01.cartodb.com/api/v2/sql?q='+sql, function(error, topology) { + for (var i = 0; i < Object.keys(topology.objects).length; i++) { + var iso = topology.objects[i].properties.iso; + + var bounds = draw(topology, i, iso, { alerts: false }); + + if (iso === 'CHN') { + that.bounds = bounds; + + d3.json('https://wri-01.cartodb.com/api/v2/sql?q='+sql_, function(error, topology) { + draw(topology, 0, 'CHN', { alerts: false, bounds: that.bounds}); + }); + } + } + }); + } +}); \ No newline at end of file diff --git a/app/assets/javascripts/countries/views/overview.js b/app/assets/javascripts/countries/views/overview.js new file mode 100644 index 0000000000..31e00f1ff4 --- /dev/null +++ b/app/assets/javascripts/countries/views/overview.js @@ -0,0 +1,1631 @@ +gfw.ui.view.CountriesOverview = cdb.core.View.extend({ + el: document.body, + + events: { + 'click .info': '_openSource', + 'click .graph_tab': '_updateGraph', + 'click .countries_list__footer': '_drawList' + }, + + initialize: function() { + this.model = new gfw.ui.model.CountriesOverview(); + + this.$graph = $('.overview_graph__area'); + this.$years = $('.overview_graph__years'); + + var m = this.m = 40, + w = this.w = this.$graph.width()+(m*2), + h = this.h = this.$graph.height(), + vertical_m = this.vertical_m = 20; + + this.x_scale = d3.scale.linear() + .range([m, w-m]) + .domain([2001, 2012]); + + this.grid_scale = d3.scale.linear() + .range([vertical_m, h-vertical_m]) + .domain([0, 1]); + + this.model.bind('change:graph', this._redrawGraph, this); + this.model.bind('change:years', this._toggleYears, this); + this.model.bind('change:class', this._toggleClass, this); + + this._initViews(); + }, + + _initViews: function() { + this.sourceWindow = new gfw.ui.view.SourceWindow(); + this.$el.append(this.sourceWindow.render()); + + this.tooltip = d3.select('body') + .append('div') + .attr('class', 'tooltip'); + + this._drawYears(); + this._drawGraph(); + this._drawList(); + + Share = new gfw.ui.view.Share(); + this.$el.find('.overview_graph').append(Share.render()); + }, + + _openSource: function(e) { + e.preventDefault(); + + var source = $(e.target).closest('.info').attr('data-source'); + + ga('send', 'event', 'SourceWindow', 'Open', source); + this.sourceWindow.show(source).addScroll(); + }, + + _toggleYears: function() { + var that = this; + + if (this.model.get('years') === false) { + this.$years.slideUp(250, function() { + $('.overview_graph__axis').slideDown(); + }); + } else { + $('.overview_graph__axis').slideUp(250, function() { + that.$years.slideDown(); + }); + } + }, + + _showYears: function() { + if (!this.model.get('years')) { + this.model.set('years', true); + } + }, + + _hideYears: function() { + if (this.model.get('years')) { + this.model.set('years', false); + } + }, + + _updateGraph: function(e) { + e.preventDefault(); + + var $target = $(e.target).closest('.graph_tab'), + graph = $target.attr('data-slug'); + + if (graph === this.model.get('graph')) { + return; + } else { + $('.graph_tab').removeClass('selected'); + $target.addClass('selected'); + + this.model.set('graph', graph); + } + }, + + _redrawGraph: function() { + var graph = this.model.get('graph'); + + $('.overview_graph__title').html(config.GRAPHS[graph].title); + $('.overview_graph__legend p').html(config.GRAPHS[graph].subtitle); + $('.overview_graph__legend .info').attr('data-source', graph); + + this.$graph.find('.'+graph); + + this.$graph.find('.chart').hide(); + this.$graph.find('.'+graph).fadeIn(); + + this._drawGraph(); + this._drawList(); + }, + + _drawList: function(e) { + var that = this; + + e && e.preventDefault(); + + if (this.model.get('graph') === 'total_loss') { + var sql = 'WITH loss as (SELECT iso, SUM('; + + for(var y = 2001; y < 2012; y++) { + sql += 'y'+y+' + '; + } + + sql += 'y2012) as sum_loss\ + FROM countries_loss\ + GROUP BY iso)'; + + sql += 'SELECT c.iso, c.name, c.enabled, sum_loss\ + FROM loss, gfw2_countries c\ + WHERE loss.iso = c.iso\ + AND NOT sum_loss = 0\ + ORDER BY sum_loss DESC '; + + if (e) { + sql += 'OFFSET 10'; + } else { + sql += 'LIMIT 10'; + } + + d3.json('http://wri-01.cartodb.com/api/v2/sql/?q='+encodeURIComponent(sql), function(json) { + var self = that, + markup_list = ''; + + var data = json.rows; + + _.each(data, function(val, key) { + var ord = e ? (key+11) : (key+1), + enabled = val.enabled ? ''+val.name+'' : val.name; + + markup_list += '
  • \ +
    \ +
    '+ord+'
    \ +
    '+enabled+'
    \ +
  • '; + }); + + if (e) { + $('.countries_list__footer').fadeOut(); + } else { + $('.countries_list ul').html(''); + $('.countries_list__footer').show(); + + $('.countries_list__header__minioverview').html('Loss vs Gain'); + } + + $('.countries_list ul').append(markup_list); + + that.model.set('class', null); + + _.each(data, function(val, key) { + self._drawMiniOverview(val.iso); + }); + }); + } else if (this.model.get('graph') === 'percent_loss') { + var sql = 'SELECT c.iso, c.name, c.enabled, loss_y2001_y2012 as ratio_loss\ + FROM countries_percent percent, gfw2_countries c\ + WHERE percent.iso = c.iso AND c.enabled IS true\ + AND NOT loss_y2001_y2012 = 0\ + ORDER BY ratio_loss DESC '; + + if (e) { + sql += 'OFFSET 10'; + } else { + sql += 'LIMIT 10'; + } + + d3.json('http://wri-01.cartodb.com/api/v2/sql/?q='+encodeURIComponent(sql), function(json) { + var self = that, + markup_list = ''; + + var data = json.rows; + + _.each(data, function(val, key) { + var ord = e ? (key+11) : (key+1), + enabled = val.enabled ? ''+val.name+'' : val.name; + + markup_list += '
  • \ +
    \ +
    '+ord+'
    \ +
    '+enabled+'
    \ +
  • '; + }); + + if (e) { + $('.countries_list__footer').fadeOut(); + } else { + $('.countries_list ul').html(''); + $('.countries_list__footer').show(); + + $('.countries_list__header__minioverview').html('% Loss'); + } + + $('.countries_list ul').append(markup_list); + + that.model.set('class', null); + + _.each(data, function(val, key) { + self._drawMiniOverview(val.iso); + }); + }); + } else if (this.model.get('graph') === 'total_extent') { + var sql = 'WITH extent as (SELECT iso, SUM('; + + for(var y = 2001; y < 2012; y++) { + sql += 'y'+y+' + '; + } + + sql += 'y2012) as sum_extent\ + FROM extent_gt_25\ + GROUP BY iso)'; + + sql += 'SELECT c.iso, c.name, c.enabled, sum_extent\ + FROM extent, gfw2_countries c\ + WHERE extent.iso = c.iso\ + AND NOT sum_extent = 0\ + ORDER BY sum_extent DESC '; + + if (e) { + sql += 'OFFSET 10'; + } else { + sql += 'LIMIT 10'; + } + + d3.json('http://wri-01.cartodb.com/api/v2/sql/?q='+encodeURIComponent(sql), function(json) { + var self = that, + markup_list = ''; + + var data = json.rows; + + _.each(data, function(val, key) { + var ord = e ? (key+11) : (key+1), + enabled = val.enabled ? ''+val.name+'' : val.name; + + markup_list += '
  • \ +
    \ +
    '+ord+'
    \ +
    '+enabled+'
    \ +
  • '; + }); + + if (e) { + $('.countries_list__footer').fadeOut(); + } else { + $('.countries_list ul').html(''); + $('.countries_list__footer').show(); + + $('.countries_list__header__minioverview').html('Cover extent vs Cover loss'); + } + + $('.countries_list ul').append(markup_list); + + that.model.set('class', 'expanded'); + + _.each(data, function(val, key) { + self._drawMiniOverview(val.iso); + }); + }); + } else if (this.model.get('graph') === 'ratio') { + var sql = 'WITH loss as (SELECT iso, SUM('; + + for(var y = 2001; y < 2012; y++) { + sql += 'loss.y'+y+' + '; + } + + sql += 'loss.y2012) as sum_loss\ + FROM loss_gt_50 loss\ + GROUP BY iso), gain as (SELECT g.iso, SUM(y2001_y2012) as sum_gain\ + FROM countries_gain g, loss_gt_50 loss\ + WHERE loss.iso = g.iso\ + GROUP BY g.iso), ratio as ('; + + sql += 'SELECT c.iso, c.name, c.enabled, loss.sum_loss/gain.sum_gain as ratio\ + FROM loss, gain, gfw2_countries c\ + WHERE sum_gain IS NOT null\ + AND NOT sum_gain = 0\ + AND c.iso = gain.iso\ + AND c.iso = loss.iso\ + ORDER BY loss.sum_loss DESC\ + LIMIT 50) '; + + sql += 'SELECT *\ + FROM ratio\ + WHERE ratio IS NOT null\ + ORDER BY ratio DESC '; + + if (e) { + sql += ['OFFSET 10', + 'LIMIT 40'].join('\n'); + } else { + sql += 'LIMIT 10'; + } + + d3.json('http://wri-01.cartodb.com/api/v2/sql/?q='+encodeURIComponent(sql), function(json) { + var self = that, + markup_list = ''; + + var data = json.rows; + + _.each(data, function(val, key) { + var ord = e ? (key+11) : (key+1), + enabled = val.enabled ? ''+val.name+'' : val.name; + + markup_list += '
  • \ +
    '+formatNumber(parseFloat(val.ratio).toFixed(2))+'
    \ +
    '+ord+'
    \ +
    '+enabled+'
    \ +
  • '; + }); + + if (e) { + $('.countries_list__footer').fadeOut(); + } else { + $('.countries_list ul').html(''); + $('.countries_list__footer').show(); + + $('.countries_list__header__minioverview').html('Ratio of Loss to Gain'); + } + + $('.countries_list ul').append(markup_list); + + that.model.set('class', 'medium'); + + _.each(data, function(val, key) { + self._drawMiniOverview(val.iso); + }); + }); + } else if (this.model.get('graph') === 'domains') { + var sql = 'SELECT name, total_loss, total_gain, GREATEST(' + + for(var y = 2001; y < 2012; y++) { + sql += 'y'+y+', ' + } + + sql += 'y2012) as max\ + FROM countries_domains\ + ORDER BY total_loss DESC '; + + d3.json('http://wri-01.cartodb.com/api/v2/sql/?q='+encodeURIComponent(sql), function(json) { + var self = that, + markup_list = ''; + + var data = json.rows; + + _.each(data, function(val, key) { + markup_list += ['
  • ', + '
    ', + '
    '+formatNumber(parseFloat(val.total_loss/1000000).toFixed(1))+' Mha
    ', + '
    '+formatNumber(parseFloat(val.total_gain/1000000).toFixed(1))+' Mha
    ', + '
    ', + '
    '+(key+1)+'
    ', + '
    '+val.name+'
    ', + '
  • '].join(''); + }); + + $('.countries_list__footer').hide(); + $('.countries_list__header__minioverview').html('Total loss vs Total gain'); + $('.countries_list ul').html(markup_list); + + that.model.set('class', 'huge'); + + _.each(data, function(val, key) { + self._drawMiniOverview(val.iso); + }); + }); + } + }, + + _toggleClass: function() { + if (this.model.get('class') === 'expanded') { + $('.countries_list__header__minioverview').addClass('expanded'); + $('.countries_list__minioverview').addClass('expanded'); + + $('.countries_list__header__minioverview').removeClass('medium huge'); + $('.countries_list__minioverview').removeClass('medium huge'); + } else if (this.model.get('class') === 'medium') { + $('.countries_list__header__minioverview').addClass('medium'); + $('.countries_list__minioverview').addClass('medium'); + + $('.countries_list__header__minioverview').removeClass('expanded huge'); + $('.countries_list__minioverview').removeClass('expanded huge'); + } else if (this.model.get('class') === 'huge') { + $('.countries_list__header__minioverview').addClass('huge'); + $('.countries_list__minioverview').addClass('huge'); + + $('.countries_list__header__minioverview').removeClass('expanded medium'); + $('.countries_list__minioverview').removeClass('expanded medium'); + } else { + $('.countries_list__header__minioverview').removeClass('expanded medium huge'); + $('.countries_list__minioverview').removeClass('expanded medium huge'); + } + }, + + _drawMiniOverview: function(iso) { + var width = 90, + height = 30; + + var graph = d3.select('.countries_list__minioverview_'+iso) + .append('svg:svg') + .attr('width', width) + .attr('height', height); + + if (this.model.get('graph') === ('total_loss')) { + var sql = 'SELECT '; + + for(var y = 2001; y < 2012; y++) { + sql += 'y'+y+', ' + } + + sql += "y2012, (SELECT y2001_y2012\ + FROM countries_gain\ + WHERE c.iso = iso) as gain\ + FROM loss_gt_0 c \ + WHERE iso = '"+iso+"'"; + + d3.json('https://wri-01.cartodb.com/api/v2/sql?q='+sql, function(json) { + var data = json.rows[0]; + + var data_ = [], + gain = null; + + _.each(data, function(val, key) { + if (key === 'gain') { + gain = val/12; + } else { + data_.push({ + 'year': key.replace('y',''), + 'value': val + }); + } + }); + + var y_scale = d3.scale.linear() + .domain([0, d3.max(data_, function(d) { return d.value; })]) + .range([height, 0]); + + var barWidth = width / data_.length; + + var bar = graph.selectAll('g') + .data(data_) + .enter().append('g') + .attr('transform', function(d, i) { return 'translate(' + (i * barWidth) + ', 0)'; }); + + bar.append('svg:rect') + .attr('class', 'bar') + .attr('y', function(d) { return y_scale(d.value); }) + .attr('height', function(d) { return height - y_scale(d.value); }) + .attr('width', barWidth - 1); + + var data_gain_ = [ + { + year: 2001, + value: gain + }, + { + year: 2012, + value: gain + } + ]; + + graph.selectAll('line.minioverview_line') + .data(data_gain_) + .enter() + .append('line') + .attr({ + 'class': 'minioverview_line', + 'x1': 0, + 'x2': width, + 'y1': function(d) { return y_scale(gain); }, + 'y2': function(d) { return y_scale(gain); } + }); + }); + } else if (this.model.get('graph') === ('percent_loss')) { + var sql = 'WITH loss as (SELECT '; + + for(var y = 2001; y < 2012; y++) { + sql += 'y'+y+' as loss_y'+y+', '; + } + + sql += "y2012 as loss_y2012\ + FROM loss_gt_25\ + WHERE iso = '"+iso+"'), extent as (SELECT "; + + for(var y = 2001; y < 2012; y++) { + sql += 'y'+y+' as extent_y'+y+', '; + } + + sql += "y2012 as extent_y2012\ + FROM extent_gt_25\ + WHERE iso = '"+iso+"')"; + + sql += 'SELECT '; + + for(var y = 2001; y < 2012; y++) { + sql += 'loss_y'+y+'/extent_y'+y+' as percent_'+y+', '; + } + + sql += 'loss_y2012/extent_y2012 as percent_2012\ + FROM loss, extent'; + + d3.json('https://wri-01.cartodb.com/api/v2/sql?q='+encodeURIComponent(sql), function(json) { + var data = json.rows[0]; + + var data_ = []; + + _.each(data, function(val, key) { + data_.push({ + 'year': key.replace('y',''), + 'value': val*100 + }); + }); + + var y_scale = d3.scale.linear() + .domain([0, d3.max(data_, function(d) { return d.value; })]) + .range([height, 0]); + + var barWidth = width / data_.length; + + var bar = graph.selectAll('g') + .data(data_) + .enter().append('g') + .attr('transform', function(d, i) { return 'translate(' + (i * barWidth) + ', 0)'; }); + + bar.append('svg:rect') + .attr('class', 'bar') + .attr('y', function(d) { return y_scale(d.value); }) + .attr('height', function(d) { return height - y_scale(d.value); }) + .attr('width', barWidth - 1); + + }); + } else if (this.model.get('graph') === ('total_extent')) { + var sql = 'SELECT '; + + for(var y = 2001; y < 2012; y++) { + sql += 'loss.y'+y+' as loss_y'+y+', '; + } + + sql += 'loss.y2012 as loss_y2012, '; + + for(var y = 2001; y < 2012; y++) { + sql += 'extent.y'+y+' as extent_y'+y+', '; + } + + sql += "extent.y2012 as extent_y2012\ + FROM loss_gt_0 loss, extent_gt_25 extent\ + WHERE loss.iso = extent.iso\ + AND loss.iso = '"+iso+"'"; + + d3.json('https://wri-01.cartodb.com/api/v2/sql?q='+sql, function(json) { + + var graph2 = d3.select('.countries_list__minioverview_'+iso) + .append('div') + .attr('class', 'sibling') + .append('svg:svg') + .attr('width', width) + .attr('height', height); + + var data = json.rows[0]; + + var data_loss_ = [], + data_extent_ = []; + + _.each(data, function(val, key) { + if (key.indexOf('loss_y') != -1) { + data_loss_.push({ + 'year': key.split('_y')[1], + 'value': val + }); + } + + if (key.indexOf('extent_y') != -1) { + data_extent_.push({ + 'year': key.split('extent_y')[1], + 'value': val + }); + } + }); + + var y_scale_loss = d3.scale.linear() + .domain([0, d3.max(data_loss_, function(d) { return d.value; })]) + .range([height, 0]); + + var y_scale_extent = d3.scale.linear() + .domain([0, d3.max(data_extent_, function(d) { return d.value; })]) + .range([height, 0]); + + var barWidth_loss = width / data_loss_.length; + + var bar = graph.selectAll('g') + .data(data_loss_) + .enter() + .append('g') + .attr('transform', function(d, i) { return 'translate(' + (i * barWidth_loss) + ', 0)'; }); + + bar.append('svg:rect') + .attr('class', 'bar') + .attr('y', function(d) { return y_scale_loss(d.value); }) + .attr('height', function(d) { return height - y_scale_loss(d.value); }) + .attr('width', barWidth_loss - 1); + + var barWidth_extent = width / data_extent_.length; + + var bar2 = graph2.selectAll('g') + .data(data_extent_) + .enter() + .append('g') + .attr('transform', function(d, i) { return 'translate(' + (i * barWidth_extent) + ', 0)'; }); + + bar2.append('svg:rect') + .attr('class', 'bar extent') + .attr('y', function(d) { return y_scale_extent(d.value); }) + .attr('height', function(d) { return height - y_scale_extent(d.value); }) + .attr('width', barWidth_extent - 1); + }); + } + }, + + _drawYears: function() { + var markup_years = ''; + + for (var y = 2001; y<=2012; y += 1) { + var y_ = this.x_scale(y); + + if (y === 2001) { + y_ -= 25; + } else if (y === 2012) { + y_ -= 55; + } else { + y_ -= 40; + } + + markup_years += ''+y+''; + } + + this.$years.html(markup_years); + }, + + _drawGraph: function() { + var that = this; + + var w = this.w, + h = this.h, + vertical_m = this.vertical_m, + m = this.m, + x_scale = this.x_scale; + + var grid_scale = d3.scale.linear() + .range([vertical_m, h-vertical_m]) + .domain([1, 0]); + + d3.select('#chart').remove(); + + var svg = d3.select('.overview_graph__area') + .append('svg:svg') + .attr('id', 'chart') + .attr('width', w) + .attr('height', h); + + // grid + svg.selectAll('line.grid_h') + .data(grid_scale.ticks(4)) + .enter() + .append('line') + .attr({ + 'class': 'grid grid_h', + 'x1': 0, + 'x2': w, + 'y1': function(d, i) { return grid_scale(d); }, + 'y2': function(d, i) { return grid_scale(d); } + }); + + svg.selectAll('line.grid_v') + .data(x_scale.ticks(12)) + .enter() + .append('line') + .attr({ + 'class': 'grid grid_v', + 'y1': h, + 'y2': 0, + 'x1': function(d) { return x_scale(d); }, + 'x2': function(d) { return x_scale(d); } + }); + + var gradient = svg.append('svg:defs') + .append('svg:linearGradient') + .attr('id', 'gradient') + .attr('x1', '0%') + .attr('y1', '0%') + .attr('x2', '0%') + .attr('y2', '100%') + .attr('spreadMethod', 'pad'); + + gradient.append('svg:stop') + .attr('offset', '0%') + .attr('stop-color', '#CA46FF') + .attr('stop-opacity', .5); + + gradient.append('svg:stop') + .attr('offset', '100%') + .attr('stop-color', '#D24DFF') + .attr('stop-opacity', 1); + + if (this.model.get('graph') === 'total_loss') { + this._showYears(); + + svg.append('text') + .attr('class', 'axis') + .attr('id', 'axis_y') + .text('Mha') + .attr('x', -h/2) + .attr('y', 30) + .attr('transform', 'rotate(-90)'); + + var sql = 'SELECT '; + + for(var y = 2001; y < 2012; y++) { + sql += 'SUM(y'+y+') as y'+y+', ' + } + + sql += 'SUM(y2012) as y2012, (SELECT SUM(y2001_y2012)\ + FROM countries_gain) as gain\ + FROM loss_gt_0'; + + d3.json('https://wri-01.cartodb.com/api/v2/sql?q='+sql, function(error, json) { + var data = json.rows[0]; + + var data_ = [], + gain = null; + + _.each(data, function(val, key) { + if (key === 'gain') { + gain = val/12; + } else { + data_.push({ + 'year': key.replace('y',''), + 'value': val + }); + } + }); + + var y_scale = d3.scale.linear() + .range([vertical_m, h-vertical_m]) + .domain([d3.max(data_, function(d) { return d.value; }), 0]); + + // area + var area = d3.svg.area() + .x(function(d) { return x_scale(d.year); }) + .y0(h) + .y1(function(d) { return y_scale(d.value); }); + + svg.append('path') + .datum(data_) + .attr('class', 'area') + .attr('d', area) + .style('fill', 'url(#gradient)'); + + // circles + svg.selectAll('circle') + .data(data_) + .enter() + .append('svg:circle') + .attr('class', 'linedot') + .attr('cx', function(d) { + return x_scale(d.year); + }) + .attr('cy', function(d){ + return y_scale(d.value); + }) + .attr('r', 6) + .attr('name', function(d) { + return ''+d.year+''+formatNumber(parseFloat(d.value/1000000).toFixed(1))+' Mha'; + }) + .on('mouseover', function(d) { + that.tooltip.html($(this).attr('name')) + .style('visibility', 'visible') + .style('top', $(this).offset().top-100+'px') + .style('left', $(this).offset().left-$('.tooltip').width()/2-4+'px') + .attr('class', 'tooltip'); + + d3.select(this) + .transition() + .duration(100) + .attr('r', 7); + + // TODO: highlighting the legend + }) + .on('mouseout', function(d) { + that.tooltip.style('visibility', 'hidden'); + + d3.select(this) + .transition() + .duration(100) + .attr('r', 6); + + // TODO: highlighting the legend + }); + + var data_gain_ = [ + { + year: 2001, + value: gain + }, + { + year: 2012, + value: gain + } + ]; + + // line + svg.selectAll('line.overview_line') + .data([data_gain_[0]]) + .enter() + .append('line') + .attr({ + 'class': 'overview_line', + 'x1': m, + 'x2': w-m, + 'y1': function(d) { return y_scale(gain); }, + 'y2': function(d) { return y_scale(gain); } + }); + + svg.selectAll('circle.gain') + .data(data_gain_) + .enter() + .append('svg:circle') + .attr('class', 'linedot gain') + .attr('cx', function(d) { + return x_scale(d.year); + }) + .attr('cy', function(d){ + return y_scale(d.value); + }) + .attr('r', 6) + .attr('name', function(d) { + return '2001-2012'+formatNumber(parseFloat(d.value/1000000).toFixed(1))+' Mha'; + }) + .on('mouseover', function(d) { + that.tooltip.html($(this).attr('name')) + .style('visibility', 'visible') + .style('top', $(this).offset().top-100+'px') + .style('left', $(this).offset().left-$('.tooltip').width()/2-4+'px') + .attr('class', 'tooltip gain_tooltip'); + + d3.select(this) + .transition() + .duration(100) + .attr('r', 7); + + // TODO: highlighting the legend + }) + .on('mouseout', function(d) { + that.tooltip.style('visibility', 'hidden'); + + d3.select(this) + .transition() + .duration(100) + .attr('r', 6); + + // TODO: highlighting the legend + }); + }); + } else if (this.model.get('graph') === 'percent_loss') { + this._showYears(); + + svg.append('text') + .attr('class', 'axis') + .attr('id', 'axis_y') + .text('%') + .attr('x', -h/2) + .attr('y', 30) + .attr('transform', 'rotate(-90)'); + + var sql = 'WITH loss as (SELECT '; + + for(var y = 2001; y < 2012; y++) { + sql += 'SUM(y'+y+') as sum_loss_y'+y+', '; + } + + sql += 'SUM(y2012) as sum_loss_y2012\ + FROM loss_gt_25), extent as (SELECT '; + + for(var y = 2001; y < 2012; y++) { + sql += 'SUM(y'+y+') as sum_extent_y'+y+', '; + } + + sql += 'SUM(y2012) as sum_extent_y2012\ + FROM extent_gt_25)\ + SELECT '; + + for(var y = 2001; y < 2012; y++) { + sql += 'sum_loss_y'+y+'/sum_extent_y'+y+' as percent_loss_'+y+', '; + } + + sql += 'sum_loss_y2012/sum_extent_y2012 as percent_loss_2012, (SELECT SUM(y2001_y2012)/('; + + for(var y = 2001; y < 2012; y++) { + sql += 'sum_extent_y'+y+' + '; + } + + sql += 'sum_extent_y2012)\ + FROM countries_gain) as gain\ + FROM loss, extent'; + + d3.json('https://wri-01.cartodb.com/api/v2/sql?q='+encodeURIComponent(sql), function(json) { + var data = json.rows[0]; + + var data_ = [], + gain = null; + + _.each(data, function(val, key) { + if (key === 'gain') { + gain = val/12; + } else { + data_.push({ + 'year': key.replace('percent_loss_',''), + 'value': val + }); + } + }); + + var y_scale = grid_scale; + + // area + var area = d3.svg.area() + .x(function(d) { return x_scale(d.year); }) + .y0(h) + .y1(function(d) { return y_scale(d.value*100); }); + + svg.append('path') + .datum(data_) + .attr('class', 'area') + .attr('d', area) + .style('fill', 'url(#gradient)'); + + // circles + svg.selectAll('circle') + .data(data_) + .enter() + .append('svg:circle') + .attr('class', 'linedot') + .attr('cx', function(d) { + return x_scale(d.year); + }) + .attr('cy', function(d){ + return y_scale(d.value*100); + }) + .attr('r', 6) + .attr('name', function(d) { + return ''+d.year+''+parseFloat(d.value*100).toFixed(2)+' %'; + }) + .on('mouseover', function(d) { + that.tooltip.html($(this).attr('name')) + .style('visibility', 'visible') + .style('top', $(this).offset().top-100+'px') + .style('left', $(this).offset().left-$('.tooltip').width()/2-4+'px') + .attr('class', 'tooltip'); + + d3.select(this) + .transition() + .duration(100) + .attr('r', 7); + + // TODO: highlighting the legend + }) + .on('mouseout', function(d) { + that.tooltip.style('visibility', 'hidden'); + + d3.select(this) + .transition() + .duration(100) + .attr('r', 6); + + // TODO: highlighting the legend + }); + + var data_gain_ = [ + { + year: 2001, + value: gain + }, + { + year: 2012, + value: gain + } + ]; + + // line + svg.selectAll('line.overview_line') + .data([data_gain_[0]]) + .enter() + .append('line') + .attr({ + 'class': 'overview_line', + 'x1': m, + 'x2': w-m, + 'y1': function(d) { return y_scale(gain*100); }, + 'y2': function(d) { return y_scale(gain*100); } + }); + + // circles + svg.selectAll('circle.gain') + .data(data_gain_) + .enter() + .append('svg:circle') + .attr('class', 'linedot gain') + .attr('cx', function(d) { + return x_scale(d.year); + }) + .attr('cy', function(d){ + return y_scale(d.value*100); + }) + .attr('r', 6) + .attr('name', function(d) { + return '2001-2012'+parseFloat(d.value*100).toFixed(2)+' %'; + }) + .on('mouseover', function(d) { + that.tooltip.html($(this).attr('name')) + .style('visibility', 'visible') + .style('top', $(this).offset().top-100+'px') + .style('left', $(this).offset().left-$('.tooltip').width()/2-4+'px') + .attr('class', 'tooltip gain_tooltip'); + + d3.select(this) + .transition() + .duration(100) + .attr('r', 7); + + // TODO: highlighting the legend + }) + .on('mouseout', function(d) { + that.tooltip.style('visibility', 'hidden'); + + d3.select(this) + .transition() + .duration(100) + .attr('r', 6); + + // TODO: highlighting the legend + }); + }); + } else if (this.model.get('graph') === 'total_extent') { + this._showYears(); + + svg.append('text') + .attr('class', 'axis') + .attr('id', 'axis_y') + .text('Mha') + .attr('x', -h/2) + .attr('y', 30) + .attr('transform', 'rotate(-90)'); + + var gradient_extent = svg.append('svg:defs') + .append('svg:linearGradient') + .attr('id', 'gradient_extent') + .attr('x1', '0%') + .attr('y1', '0%') + .attr('x2', '0%') + .attr('y2', '100%') + .attr('spreadMethod', 'pad'); + + gradient_extent.append('svg:stop') + .attr('offset', '0%') + .attr('stop-color', '#98BD17') + .attr('stop-opacity', .5); + + gradient_extent.append('svg:stop') + .attr('offset', '100%') + .attr('stop-color', '#98BD17') + .attr('stop-opacity', 1); + + var sql = 'SELECT '; + + for(var y = 2001; y < 2012; y++) { + sql += 'SUM(loss.y'+y+') as loss_y'+y+', '; + } + + sql += 'SUM(loss.y2012) as loss_y2012, '; + + for(var y = 2001; y < 2012; y++) { + sql += 'SUM(extent.y'+y+') as extent_y'+y+', '; + } + + sql += 'SUM(extent.y2012) as extent_y2012\ + FROM loss_gt_25 loss, extent_gt_25 extent\ + WHERE loss.iso = extent.iso'; + + d3.json('https://wri-01.cartodb.com/api/v2/sql?q='+encodeURIComponent(sql), function(json) { + var data = json.rows[0]; + + var data_ = [], + data_loss_ = [], + data_extent_ = []; + + _.each(data, function(val, key) { + var year = key.split('_y')[1]; + + var obj = _.find(data_, function(obj) { return obj.year == year; }); + + if (obj === undefined) { + data_.push({ 'year': year }); + } + + if (key.indexOf('loss_y') != -1) { + data_loss_.push({ + 'year': key.split('_y')[1], + 'value': val + }); + } + + if (key.indexOf('extent_y') != -1) { + data_extent_.push({ + 'year': key.split('extent_y')[1], + 'value': val + }); + } + }); + + _.each(data_, function(val) { + var loss = _.find(data_loss_, function(obj) { return obj.year == val.year; }), + extent = _.find(data_extent_, function(obj) { return obj.year == val.year; }); + + _.extend(val, { 'loss': loss.value, 'extent': extent.value }); + }); + + var domain = [d3.max(data_, function(d) { return d.extent; }), 0]; + + var y_scale = d3.scale.linear() + .range([vertical_m, h-vertical_m]) + .domain(domain); + + // area + var area_loss = d3.svg.area() + .x(function(d) { return x_scale(d.year); }) + .y0(h) + .y1(function(d) { return y_scale(d.loss); }); + + var area_extent = d3.svg.area() + .x(function(d) { return x_scale(d.year); }) + .y0(function(d) { return y_scale(d.extent); }) + .y1(function(d) { return y_scale(d.loss); }); + + svg.append('path') + .datum(data_) + .attr('class', 'area') + .attr('d', area_loss) + .style('fill', 'url(#gradient)'); + + svg.append('path') + .datum(data_) + .attr('class', 'area') + .attr('d', area_extent) + .style('fill', 'url(#gradient_extent)'); + + // circles + svg.selectAll('circle') + .data(data_loss_) + .enter() + .append('svg:circle') + .attr('class', 'linedot') + .attr('cx', function(d) { + return x_scale(d.year); + }) + .attr('cy', function(d){ + return y_scale(d.value); + }) + .attr('r', 6) + .attr('name', function(d) { + return ''+d.year+''+formatNumber(parseFloat(d.value/1000000).toFixed(1))+' Mha'; + }) + .on('mouseover', function(d) { + that.tooltip.html($(this).attr('name')) + .style('visibility', 'visible') + .style('top', $(this).offset().top-100+'px') + .style('left', $(this).offset().left-$('.tooltip').width()/2-4+'px') + .attr('class', 'tooltip'); + + d3.select(this) + .transition() + .duration(100) + .attr('r', 7); + + // TODO: highlighting the legend + }) + .on('mouseout', function(d) { + that.tooltip.style('visibility', 'hidden'); + + d3.select(this) + .transition() + .duration(100) + .attr('r', 6); + + // TODO: highlighting the legend + }); + + svg.selectAll('circle.gain') + .data(data_extent_) + .enter() + .append('svg:circle') + .attr('class', 'linedot gain') + .attr('cx', function(d) { + return x_scale(d.year); + }) + .attr('cy', function(d){ + return y_scale(d.value); + }) + .attr('r', 6) + .attr('name', function(d) { + return ''+d.year+''+formatNumber(parseFloat(d.value/1000000).toFixed(1))+' Mha'; + }) + .on('mouseover', function(d) { + that.tooltip.html($(this).attr('name')) + .style('visibility', 'visible') + .style('top', $(this).offset().top-100+'px') + .style('left', $(this).offset().left-$('.tooltip').width()/2-4+'px') + .attr('class', 'tooltip gain_tooltip'); + + d3.select(this) + .transition() + .duration(100) + .attr('r', 7); + + // TODO: highlighting the legend + }) + .on('mouseout', function(d) { + that.tooltip.style('visibility', 'hidden'); + + d3.select(this) + .transition() + .duration(100) + .attr('r', 6); + + // TODO: highlighting the legend + }); + }); + } else if (this.model.get('graph') === 'ratio') { + this._hideYears(); + + svg.append('text') + .attr('class', 'axis light') + .attr('id', 'axis_y') + .text('Ratio of tree cover loss to gain 2001-2012') + .attr('x', -(h/2)-60) + .attr('y', 30) + .attr('transform', 'rotate(-90)'); + + svg.append('text') + .attr('class', 'axis light') + .attr('id', 'axis_ratio') + .text('1') + .attr('x', 25) + .attr('y', h-60); + + var shadow = svg.append('svg:defs') + .append('svg:filter') + .attr('id', 'shadow') + .attr('x', '0%') + .attr('y', '0%') + .attr('width', '200%') + .attr('height', '200%'); + + shadow.append('svg:feOffset') + .attr('result', 'offOut') + .attr('in', 'SourceAlpha') + .attr('dx', 0) + .attr('dy', 0); + + shadow.append('svg:feGaussianBlur') + .attr('result', 'blurOut') + .attr('in', 'offOut') + .attr('stdDeviation', 1); + + shadow.append('svg:feBlend') + .attr('in', 'SourceGraphic') + .attr('in2', 'blurOut') + .attr('mode', 'normal'); + + var sql = 'WITH loss as (SELECT iso, SUM('; + + for(var y = 2001; y < 2012; y++) { + sql += 'loss.y'+y+' + '; + } + + sql += ['loss.y2012) as sum_loss', + 'FROM loss_gt_50 loss', + 'GROUP BY iso), gain as ('].join(' '); + + sql += ['SELECT g.iso, SUM(y2001_y2012) as sum_gain', + 'FROM countries_gain g, loss_gt_50 loss', + 'WHERE loss.iso = g.iso', + 'GROUP BY g.iso), ratio as ('].join(' '); + + sql += ['SELECT c.iso, c.name, c.enabled, loss.sum_loss as loss,', + 'gain.sum_gain as gain, loss.sum_loss/gain.sum_gain as ratio', + 'FROM loss, gain, gfw2_countries c', + 'WHERE sum_gain IS NOT null', + 'AND NOT sum_gain = 0', + 'AND c.iso = gain.iso', + 'AND c.iso = loss.iso', + 'ORDER BY loss.sum_loss DESC', + 'LIMIT 50), extent as ('].join(' '); + + sql += ['SELECT extent.iso, SUM(extent.y2012) as extent', + 'FROM countries_extent extent', + 'GROUP BY extent.iso) '].join(' '); + + sql += ['SELECT *', + 'FROM ratio, extent', + 'WHERE ratio IS NOT null', + 'AND extent.iso = ratio.iso'].join(' '); + + d3.json('https://wri-01.cartodb.com/api/v2/sql?q='+encodeURIComponent(sql), function(json) { + var data = json.rows; + + var log_m = 50; + + var x_log_scale = d3.scale.log() + .range([m, w-m]) + .domain([d3.min(data, function(d) { return d.extent; }), d3.max(data, function(d) { return d.extent; })]); + + var y_log_scale = d3.scale.log() + .range([h-log_m, m]) + .domain([d3.min(data, function(d) { return d.ratio; }), d3.max(data, function(d) { return d.ratio; })]); + + var color_scale = d3.scale.linear() + .domain([d3.min(data, function(d) { return d.ratio; }), 1, 10, d3.max(data, function(d) { return d.ratio; })]) + .range(["#9ABF00", "#9ABF00", "#CA46FF", "#CA46FF"]); + + // line + svg.selectAll('line.linear_regression') + .data([1]) + .enter() + .append('line') + .attr({ + 'class': 'linear_regression', + 'x1': m, + 'x2': w-m, + 'y1': function(d) { return y_log_scale(d); }, + 'y2': function(d) { return y_log_scale(d); }, + "stroke-width": 1.3, + "stroke": "white", + "stroke-dasharray": "7,5" + }); + + // circles w/ magic numbers :( + var circle_attr = { + 'cx': function(d) { return x_log_scale(d.extent) }, + 'cy': function(d) { return y_log_scale(d.ratio) }, + 'r': 5, + 'name': function(d) { return d.name; }, + 'class': function(d) { return d.enabled ? 'ball ball_link' : 'ball ball_nolink'; } + }; + + var data_ = [], + data_link_ = [], + exclude = []; + + _.each(data, function(row) { + if (!_.contains(exclude, row.name)) { + if (row.enabled === true) { + data_link_.push(row); + } else { + data_.push(row); + } + } + }); + + var circles_link = svg.selectAll('circle.ball_link') + .data(data_link_) + .enter() + .append('a') + .attr('xlink:href', function(d) { return '/country/' + d.iso}) + .append('svg:circle') + .attr(circle_attr) + .style('fill', function(d) { + return color_scale(d.ratio); + }) + .style('filter', 'url(#shadow)') + .on('mouseover', function() { + d3.select(d3.event.target) + .transition() + .attr('r', 7) + .style('opacity', 1); + + var t = $(this).offset().top - 80, + l = $(this).offset().left, + r = $(this).attr('r'), + tip = $('.tooltip').width()/2; + + that.tooltip.html($(this).attr('name')) + .style('visibility', 'visible') + .style('top', parseInt(t, 10)+'px') + .style('left', parseInt(l, 10)+parseInt(r, 10)-parseInt(tip, 10)-10+'px') + .attr('class', 'tooltip gain_tooltip'); + }) + .on('mouseenter', function() { + d3.select(d3.event.target) + .transition() + .attr('r', 7) + .style('opacity', 1); + + var t = $(this).offset().top - 80, + l = $(this).offset().left, + r = $(this).attr('r'), + tip = $('.tooltip').width()/2; + + that.tooltip.html($(this).attr('name')) + .style('visibility', 'visible') + .style('top', parseInt(t, 10)+'px') + .style('left', parseInt(l, 10)+parseInt(r, 10)-parseInt(tip, 10)+'px') + .attr('class', 'tooltip gain_tooltip'); + }) + .on('mouseout', function() { + d3.select(d3.event.target) + .transition() + .attr('r', 5) + .style('opacity', .8); + + that.tooltip.style('visibility', 'hidden'); + }); + + var circles = svg.selectAll('circle.ball_nolink') + .data(data_) + .enter() + .append('svg:circle') + .attr(circle_attr) + .style('fill', function(d) { + return color_scale(d.ratio); + }) + .style('filter', 'url(#shadow)') + .on('mouseover', function() { + d3.select(d3.event.target) + .transition() + .attr('r', 7) + .style('opacity', 1); + + var t = $(this).offset().top - 80, + l = $(this).offset().left, + r = $(this).attr('r'), + tip = $('.tooltip').width()/2; + + that.tooltip.html($(this).attr('name')) + .style('visibility', 'visible') + .style('top', parseInt(t, 10)+'px') + .style('left', parseInt(l, 10)+parseInt(r, 10)-parseInt(tip, 10)-10+'px') + .attr('class', 'tooltip gain_tooltip'); + }) + .on('mouseenter', function() { + d3.select(d3.event.target) + .transition() + .attr('r', 7) + .style('opacity', 1); + + var t = $(this).offset().top - 80, + l = $(this).offset().left, + r = $(this).attr('r'), + tip = $('.tooltip').width()/2; + + that.tooltip.html($(this).attr('name')) + .style('visibility', 'visible') + .style('top', parseInt(t, 10)+'px') + .style('left', parseInt(l, 10)+parseInt(r, 10)-parseInt(tip, 10)+'px') + .attr('class', 'tooltip gain_tooltip'); + }) + .on('mouseout', function() { + d3.select(d3.event.target) + .transition() + .attr('r', 5) + .style('opacity', .8); + + that.tooltip.style('visibility', 'hidden'); + }); + }); + } else if (this.model.get('graph') === 'domains') { + this._showYears(); + + var sql = 'SELECT name, '; + + for(var y = 2001; y < 2012; y++) { + sql += 'y'+y+', ' + } + + sql += 'y2012, GREATEST(' + + for(var y = 2001; y < 2012; y++) { + sql += 'y'+y+', ' + } + + sql += 'y2012) as max\ + FROM countries_domains'; + + d3.json('https://wri-01.cartodb.com/api/v2/sql?q='+sql, function(error, json) { + var data = json.rows; + + var r_scale = d3.scale.linear() + .range([5, 30]) // max ball radius + .domain([0, d3.max(data, function(d) { return d.max; })]) + + for(var j = 0; j < data.length; j++) { + var data_ = [], + domain = ''; + + _.each(data[j], function(val, key) { + if (key !== 'max') { + if (key === 'name') { + domain = val.toLowerCase(); + } else { + data_.push({ + 'year': key.replace('y',''), + 'value': val + }); + } + } + }); + + svg.append('text') + .attr('class', 'label') + .attr('id', 'label_'+domain) + .text(domain) + .attr('x', function() { + var l = x_scale(2002) - $(this).width()/2; + + return l; + }) + .attr('y', (h/5)*(j+.6)); + + var circle_attr = { + 'cx': function(d, i) { return x_scale(2001 + i); }, + 'cy': function(d) { return (h/5)*(j+1); }, + 'r': function(d) { return r_scale(d.value); }, + 'class': function(d) { return 'ball'; } + }; + + svg.selectAll('circle.domain_'+domain) + .data(data_) + .enter() + .append('svg:circle') + .attr(circle_attr) + .attr('data-slug', domain) + .attr('name', function(d) { + return ''+d.year+''+formatNumber(parseFloat(d.value/1000000).toFixed(1))+' Mha'; + }) + .style('fill', function(d) { return config.GRAPHCOLORS[domain]; }) + .on('mouseover', function() { + d3.select(d3.event.target) + .transition() + .attr('r', function(d) { return circle_attr.r(d) + 2; }) + .style('opacity', 1); + + var t = $(this).offset().top - 100, + l = $(this).offset().left, + r = $(this).attr('r'), + tip = $('.tooltip').width()/2, + slug = $(this).attr('data-slug'); + + that.tooltip.html($(this).attr('name')) + .style('visibility', 'visible') + .style('top', parseInt(t, 10)+'px') + .style('left', parseInt(l, 10)+parseInt(r, 10)-parseInt(tip, 10)-10+'px') + .attr('class', 'tooltip') + .attr('data-slug', 'tooltip') + .style('color', function() { + if (slug === 'subtropical') { + return '#FFC926' + } else { + return config.GRAPHCOLORS[slug]; + } + }); + }) + .on('mouseenter', function() { + d3.select(d3.event.target) + .transition() + .attr('r', function(d) { return circle_attr.r(d) + 2; }) + .style('opacity', 1); + + var t = $(this).offset().top - 80, + l = $(this).offset().left, + r = $(this).attr('r'), + tip = $('.tooltip').width()/2, + slug = $(this).attr('data-slug'); + + that.tooltip.html($(this).attr('name')) + .style('visibility', 'visible') + .style('top', parseInt(t, 10)+'px') + .style('left', parseInt(l, 10)+parseInt(r, 10)-parseInt(tip, 10)-10+'px') + .attr('class', 'tooltip') + .attr('data-slug', 'tooltip') + .style('color', function() { + if (domain === 'subtropical') { return config.GRAPHCOLORS[domain]; } + }); + }) + .on('mouseout', function() { + d3.select(d3.event.target) + .transition() + .attr('r', function(d) { return circle_attr.r(d); }) + .style('opacity', .8); + + that.tooltip + .style('color', '') + .style('visibility', 'hidden'); + }); + } + }); + } + } +}); \ No newline at end of file diff --git a/app/assets/javascripts/countries/views/show.js b/app/assets/javascripts/countries/views/show.js new file mode 100644 index 0000000000..7e6001f367 --- /dev/null +++ b/app/assets/javascripts/countries/views/show.js @@ -0,0 +1,472 @@ +gfw.ui.view.CountriesShow = cdb.core.View.extend({ + el: $('.country-show'), + + events: { + 'change #areaSelector': '_onSelectArea' + }, + + initialize: function() { + var self = this; + _.bindAll(this, '_positionScroll'); + + this.iso = this.options.iso; + this.$nav = this.$('.country-nav'); + this.$indepth = this.$('.country-indepth'); + this.$areaSelector = this.$('#areaSelector'); + + this.country = new gfw.ui.model.Country({ iso: this.iso }); + + this._setAreaSelector(); + this._stickynav(); + this._initViews(); + + var sql = new cartodb.SQL({ user: 'wri-01' }); + sql.execute("SELECT bounds FROM country_mask WHERE code = '" + this.country.get('iso') + "'") + .done(function(data) { + var bounds = JSON.parse(data.rows[0].bounds), + geojson = L.geoJson(bounds); + + //self.map.fitBounds(geojson.getBounds()); + self._initMap(geojson.getBounds()); + }); + }, + + _initViews: function() { + this._drawTenure(); + this._drawForestsType(); + this._drawFormaAlerts(); + }, + + _setAreaSelector: function() { + var self = this; + + this.country.fetch({ + success: function(model, response, options) { + _.each(model.get('areas').models, function(area) { + self.$areaSelector.append('') + }); + }, + error: function() {} + }); + }, + + _onSelectArea: function() { + var areaName = this.$areaSelector.val(); + var area = this.country.get('areas').where({ name_1: areaName })[0]; + this._highlightArea(area); + }, + + _initMap: function(bounds) { + var self = this; + + this.map = new L.Map('countryMap', { + center: [0, 0], + maxBounds: bounds, + zoom: 3, + zoomControl: false, + dragging: false, + touchZoom: false, + doubleClickZoom: false, + scrollWheelZoom: false, + attributionControl: false + }); + + // set forest cover layer + L.tileLayer('http://earthengine.google.org/static/hansen_2013/tree_alpha/{z}/{x}/{y}.png') + .addTo(this.map); + + // set country layer + cartodb.createLayer(this.map, { + user_name: 'wri-01', + type: 'cartodb', + cartodb_logo: false, + sublayers: [{ + sql: "SELECT * FROM country_mask", + cartocss: "\ + #country_mask {\ + polygon-fill: #2D2B36;\ + polygon-opacity: 1;\ + line-color: #2D2B36;\ + line-width: 1;\ + line-opacity: 1;\ + }\ + #country_mask[code='" + this.country.get('iso') + "'] {\ + polygon-fill: #FF6600;\ + polygon-opacity: 0;\ + line-color: #FFFFFF;\ + line-width: 0;\ + line-opacity: 1;\ + }" + }] + }) + .addTo(this.map) + .done(function(layer) { + }); + }, + + _highlightArea: function(area) { + }, + + _positionScroll: function() { + this.indepth_bar = 0; + + var h_min = $('.country-alerts').offset().top - 48, + h_max = $('.country-conventions').offset().top - 46; + + // if (this.$('.country-indepth').length > 0) { + // this.indepth_bar = 48; + + // h_min -= 48; + // h_max -= 48; + + // if ($(window).scrollTop() > (h_min) && $(window).scrollTop() < h_max) { + // this.$indepth.css({ + // position: 'fixed', + // top: 0 + // }); + // } else if ($(window).scrollTop() >= h_max) { + // this.$indepth.css({ + // position: 'absolute', + // top: h_max - h_min + // }); + // } else { + // this.$indepth.css({ + // position: 'absolute', + // top: 0 + // }); + // } + // } + + if ($(window).scrollTop() > (h_min) && $(window).scrollTop() < h_max) { + // fixed + this.$nav.css({ + position: 'fixed', + top: this.indepth_bar + }); + this.$nav.addClass('fixed'); + } else if ($(window).scrollTop() >= h_max) { + // dissapear + this.$nav.css({ + position: 'absolute', + top: h_max - h_min + }); + } else { + // default + this.$nav.css({ + position: 'absolute', + top: 0 + }); + this.$nav.removeClass('fixed'); + } + }, + + _stickynav: function() { + this._positionScroll(); + + $.scrollIt({ + upKey: null, + downKey: null, + easing: 'linear', + scrollTime: 400, + activeClass: 'active', + onPageChange: null, + topOffset: -48 - this.indepth_bar + }); + + $(window).scroll(this._positionScroll); + }, + + _drawTenure: function() { + var sql = ['SELECT tenure_government, tenure_owned, tenure_owned_individuals,', + 'tenure_reserved, GREATEST(tenure_government, tenure_owned,', + 'tenure_owned_individuals,', + 'tenure_owned_individuals,', + 'tenure_reserved) as max', + "FROM gfw2_countries WHERE iso = '" + this.country.get('iso') + "'"].join(' '); + + d3.json('https://wri-01.cartodb.com/api/v2/sql?q='+ sql, function(json) { + var data = json.rows[0], + h = 0; + + var x_extent = [0, data.max], + x_scale = d3.scale.linear() + .range([0, 500]) + .domain(x_extent); + + var origins = [], + aggr = 0, + klass = ['one', 'two', 'three', 'four'] + + var tenures = [ + { + name: 'Public lands administered by the government', + percent: data.tenure_government + }, + { + name: 'Public lands reserved for communities and indigenous groups', + percent: data.tenure_reserved + }, + { + name: 'Private lands owned by communities and indigenous groups', + percent: data.tenure_owned + }, + { + name: 'Private lands owned by firms and individuals', + percent: data.tenure_owned_individuals + } + ]; + + var tenures_ord = []; + + _.each(tenures, function(tenure, i) { + if (tenure['percent'] !== null && tenure['percent'] !== 0) { + if ($('.country-tenure').not(':visible')) { + $('.country-tenure').show(); + } + + h += 50; + + tenures_ord.push({ + name: tenure['name'], + percent: tenure['percent'] + }); + } + }); + + var svg = d3.select('.country-tenure .line-graph') + .append('svg') + .attr('width', 600) + .attr('height', h); + + // add lines + svg.selectAll('rect') + .data(tenures_ord) + .enter() + .append('rect') + .attr('class', function(d, i) { + return klass[i]; + }) + .attr('x', function() { + return x_scale(0); + }) + .attr('y', function(d, i) { + return 25 + (50 * i); + }) + .attr('width', function(d) { + return x_scale(d['percent']); + }) + .attr('height', 4) + .attr('rx', 2) + .attr('ry', 2); + + // add balls + svg.selectAll('circle') + .data(tenures_ord) + .enter() + .append('svg:circle') + .attr('class', function(d, i) { + return klass[i]; + }) + .attr('cx', function(d, i) { + return x_scale(d['percent']); + }) + .attr('cy', function(d, i) { + return 27 + (50 * i); + }) + .attr('r', 5); + + // add values + svg.selectAll('.units') + .data(tenures_ord) + .enter() + .append('text') + .text(function(d) { + return d['percent']/1000000 + 'Mha'; + }) + .attr('class', function(d, i) { + return 'units ' + klass[i]; + }) + .attr('x', function(d, i) { + return x_scale(d['percent'])+10; + }) + .attr('y', function(d, i) { + return 31 + (50 * i); + }); + + // add legend + svg.selectAll('.legend') + .data(tenures_ord) + .enter() + .append('text') + .text(function(d) { + return d['name']; + }) + .attr('class', function(d, i) { + return 'legend ' + klass[i]; + }) + .attr('x', 0) + .attr('y', function(d, i) { + return 15 + (50 * i); + }); + }); + }, + + _drawForestsType: function() { + var sql = ["SELECT unnest(array['forest_regenerated', 'forest_primary', 'forest_planted'])", + 'AS type, unnest(array[COALESCE(forest_regenerated, 0),', + 'COALESCE(forest_primary, 0),', + 'COALESCE(forest_planted, 0)])', + 'AS percent', + 'FROM gfw2_countries', + "WHERE iso = '" + this.country.get('iso') + "'"].join(' '); + + d3.json('https://wri-01.cartodb.com/api/v2/sql?q='+sql, function(json) { + // TODO => if percents are 0 + var data = _.pluck(json.rows, 'percent'); + + var width = 225, + height = 225, + radius = Math.min(width, height) / 2, + colors = ['#819515', '#A1BA42', '#DDDDDD'], + labelColors = ['white', 'white', '#555']; + + var pie = d3.layout.pie() + .sort(null); + + var arc = d3.svg.arc() // create elements for using arc data + .innerRadius(radius - 67) + .outerRadius(radius) + + var svg = d3.select(".forests-type-graph") + .append("svg") + .attr("width", width) + .attr("height", height) + .append("g") + .attr("transform", "translate(" + width / 2 + "," + height / 2 + ")"); + + var path = svg.selectAll("path") + .data(pie(data)); + + path.enter().append("path") + .attr("fill", function(d, i) { return colors[i]; }) + .attr("d", arc); + + path.enter().append('text') + .attr('transform', function(d) { var c = arc.centroid(d); return 'translate(' + (c[0]-12) + ',' + (c[1]+8) + ')'}) + .text(function(d) { return d.data + '%' }) + .attr('fill', function(d, i) { return labelColors[i]; } ) + .style('font-size', '13px'); + }); + }, + + _drawFormaAlerts: function() { + var that = this; + + var $graph = $('.forma-graph'); + + var width = 500, + height = 156, + h = 156, // maxHeight + radius = width / 2, + gridLinesCount = 7; + + // Add dashed grid + var graph = d3.select('.forma-graph') + .append('svg:svg') + .attr('class', 'line') + .attr('width', width) + .attr('height', height); + + var gridLineY = height; + for (var i = 0; i < gridLinesCount; i++) { + graph.append('svg:line') + .attr('x1', 0) + .attr('y1', gridLineY) + .attr('x2', width) + .attr('y2', gridLineY) + .style('stroke-dasharray', ('2, 3')) + .style('stroke', function() { return (i == 0) ? '#333' : '#CCC'; } ); + + gridLineY -= height/(gridLinesCount-1); + }; + + // Render forma graph + var sql = ["SELECT date_trunc('month', date) as date, COUNT(*) as alerts", + 'FROM forma_api', + "WHERE iso = '" + this.country.get('iso') + "'", + "GROUP BY date_trunc('month', date)", + "ORDER BY date_trunc('month', date) ASC"].join(' '); + + d3.json('https://wri-01.cartodb.com/api/v2/sql?q='+sql, function(json) { + if (json && json.rows.length > 0) { + var data = json.rows.slice(1, json.rows.length); + } else { + console.log('no data'); + //$coming_soon.show(); + return; + }; + + var x_scale = d3.scale.linear() + .domain([0, data.length - 1]) + .range([0, width - 80]); + + var max = d3.max(data, function(d) { return parseFloat(d.alerts); }); + + if (max === d3.min(data, function(d) { return parseFloat(d.alerts); })) { + h = h/2; + } + + var y_scale = d3.scale.linear() + .domain([0, max]) + .range([0, h]); + + var line = d3.svg.line() + .x(function(d, i) { return x_scale(i); }) + .y(function(d, i) { return h - y_scale(d.alerts); }) + .interpolate('basis'); + + var marginLeft = 40, + marginTop = radius - h/2; + + //$amount.html(''+formatNumber(data[data.length - 1].alerts)+''); + + var date = new Date(data[data.length - 1].date), + form_date = 'Alerts in ' + config.MONTHNAMES[date.getUTCMonth()] + ' ' + date.getUTCFullYear(); + + //$date.html(form_date); + + var cx = width - 80 + marginLeft; + var cy = h - y_scale(data[data.length - 1].alerts) + marginTop; + + graph.append('svg:path') + .attr('transform', 'translate(' + marginLeft + ',' + marginTop + ')') + .attr('d', line(data)) + .on('mousemove', function(d) { + var index = Math.round(x_scale.invert(d3.mouse(this)[0])); + + if (data[index]) { // if there's data + //$amount.html(''+formatNumber(data[index].alerts)+''); + + var date = new Date(data[index].date), + form_date = 'Alerts in ' + config.MONTHNAMES[date.getUTCMonth()] + ' ' + date.getUTCFullYear(); + + //$date.html(form_date); + + var cx = d3.mouse(this)[0] + marginLeft; + var cy = h - y_scale(data[index].alerts) + marginTop; + + graph.select('.forma_marker') + .attr('cx', cx) + .attr('cy', cy); + } + }); + + graph.append('svg:circle') + .attr('class', 'forma_marker') + .attr('cx', cx) + .attr('cy', cy) + .attr('r', 5); + }); + } + +}); diff --git a/app/assets/stylesheets/countries.scss b/app/assets/stylesheets/countries.scss index 2e1f6a3dff..05bd1cc0f2 100644 --- a/app/assets/stylesheets/countries.scss +++ b/app/assets/stylesheets/countries.scss @@ -1,3 +1,10 @@ +$primary-color: #9FBA2B; +$default-color: #463F52; +$font-light: "fira_sans_otlight", Georgia, sans-serif; +$font-regular: "fira_sans_otregular", Georgia, sans-serif; +$font-medium: "fira_sans_otmedium", Georgia, sans-serif; + +// Compass @import "compass/css3/box-shadow", "compass/css3/box-sizing", "compass/css3/border-radius", @@ -6,18 +13,20 @@ "compass/css3/images", "compass/css3/text-shadow", "compass/css3/inline-block", - "compass/utilities/sprites", - "fonts", + "compass/utilities/sprites"; + +// Global +@import "fonts", "helpers"; +// Icons @import "country-icons/*.png"; @include all-country-icons-sprites; -$primary-color: #9FBA2B; -$default-color: #463F52; -$font-light: "fira_sans_otlight", Georgia, sans-serif; -$font-regular: "fira_sans_otregular", Georgia, sans-serif; -$font-medium: "fira_sans_otmedium", Georgia, sans-serif; +// Countries +@import "countries/index", + "countries/show", + "countries/overview"; .countries { &.index .content { @@ -85,1235 +94,4 @@ $font-medium: "fira_sans_otmedium", Georgia, sans-serif; @include country-icons-sprite(country_info); &.dark { @include country-icons-sprite(country_info_dark); } -} - - -/* =Index ------------------------------------------------ */ - -.countries_block { - height: 120px; - margin-top: -10px; - background: - image-url('backgrounds/bkg_countries_block_up.png') no-repeat 0 center, - image-url('backgrounds/bkg_countries_block_down.png') repeat-x; - margin-bottom: 40px; - - .header-title { - font-size: 29px; - line-height: 120px; - color: #fff; - - a:hover { - text-decoration: underline; - } - } - span{ - color: darken($cGreen, 5%); - } -} - -.country { - float: left; - margin: 0 0 -1px -1px; - font-size: 15px; - line-height: 18px; - text-align: center; - color: #999; - - a { - position: relative; - display: block; - width: 199px; - height: 230px; - padding: 20px; - border: 1px solid #E5E5E5; - background: #fff; - - &:hover { - z-index: 1000; - margin: -10px; - padding: 30px; - @include box-shadow(0 0 5px rgba(#000, .1)); - - .country_main, - .country_alt { - fill: $cGreen; - } - - .country-content { - position: absolute; - top: 200px; left: 30px; right: 30px; - } - - svg { - top: 30px; - } - } - } - - .country-content { - position: absolute; - top: 190px; left: 20px; - right: 20px; - - span { - display: block; - color: #999; - font-size: 15px; - line-height: 1.1; - } - } - - h3 { - display: block; - margin-bottom: 5px; - font-size: 29px; - line-height: 1.1; - @extend .serif; - color: #666; - - &.small { - font-size: 19px; - } - } - - strong { - font-weight: bold; - } - - svg { - position: absolute; - top: 20px; left: 50%; - margin-left: -75px; - } -} - - -/* =Show ------------------------------------------------ */ -.country-show { - font-family: $font-regular; - color: $default-color; -} - -.country-header { - width: 100%; - border-bottom: 1px solid #CCCCCC; - - .country-header-inner { - height: 350px; - $wall-background: "backgrounds/wall_background_alpha.png"; - background: #464253; - background-image: image-url($wall-background); /* fallback */ - background-image: image-url($wall-background), -webkit-gradient(linear, left top, left bottom, from(#464253), to(#383643)); /* Saf4+, Chrome */ - background-image: image-url($wall-background), -webkit-linear-gradient(top, #464253, #383643); /* Chrome 10+, Saf5.1+ */ - background-image: image-url($wall-background), -moz-linear-gradient(top, #464253, #383643); /* FF3.6+ */ - background-image: image-url($wall-background), -ms-linear-gradient(top, #464253, #383643); /* IE10 */ - background-image: image-url($wall-background), -o-linear-gradient(top, #464253, #383643); /* Opera 11.10+ */ - background-image: image-url($wall-background), linear-gradient(to bottom, #464253, #383643); /* W3C */ - } - - .country-title { - padding: 20px; - - h1 { - color: white; - font-size: 47px; - max-width: 500px; - margin-right: 20px; - display: inline-block; - font-family: $font-regular; - } - - .country-selector { - display: inline-block; - vertical-align: top; - margin-top: 2px; - border: 2px solid #73707D; - border-radius: 40px; - height: 39px; - width: 310px; - } - - select { - border: none; - font-size: 13px; - text-transform: uppercase; - background: transparent; - color: #BEBCC2; - -webkit-appearance: none; - padding: 9px 15px; - width: 100%; - - &:focus { - outline: 0; - } - } - } // country-title - - .country-details { - background-color:rgba(0, 0, 0, 0.2); - height: 263px; - - .country-preview { - width: 735px; - height: 100%; - padding: 30px; - float: left; - @include box-sizing(border-box); - - h4 { - text-transform: uppercase; - color: white; - line-height: 18px; - font-size: 14px; - } - - .map, - .tree-numbers, - .loss-gain-graph { - float: left; - height: 100%; - } - - .map { - width: 245px; - } - - .tree-numbers { - width: 140px; - - h4 { - width: 100px; - } - - .amount, - .unit { - margin-top: 6px; - color: $primary-color; - display: inline-block; - } - - .amount { - font-size: 47px; - font-weight: bold; - } - - .total-extension { - margin-top: 18px; - } - - .total-extension .amount { - font-size: 29px; - } - } - - .loss-gain-graph { - width: 250px; - } - } - - .country-sidenav { - float: right; - width: 215px; - height: 100%; - background: #F2F2F3; - - > ul { - padding: 10px 0; - width: 100%; - - > li { - height: 53px; - width: 100%; - padding: 20px 25px; - @include box-sizing(border-box); - font-family: $font-medium; - text-transform: uppercase; - color: #464152; - font-size: 14px; - cursor: pointer; - - &:hover { - background: #FFFFFF; - } - } - } - } - - } // country-details - - .country-indepth { - clear: both; - border: 1px solid #CCCCCC; - border-bottom: 0; - height: 185px; - width: 100%; - padding: 35px 20px; - font-family: $font-medium; - @include box-sizing(border-box); - - //@include country-icons-sprite(country-indepth); - - .country-indepth__title { - font-size: 29px; - } - - .country-indepth__body { - text-transform: uppercase; - font-size: 14px; - font-weight: 700; - margin-top: 20px; - } - - .country-indepth__links { - margin-top: 25px; - color: #bbb; - font-family: $font-light; - font-size: 13px; - - a { - font-size: 14px; - display: inline-block; - vertical-align: top; - color: $primary-color; - font-family: $font-medium; - } - } - - } // country-indepth - - .country-nav-container { - position: relative; - } - - .country-nav { - width: 100%; - - > ul { - width: 100%; - text-align: center; - - > li { - @include box-sizing(border-box); - padding: 10px 15px; - height: 50px; - border: 1px solid #ccc; - width: 135px; - float: left; - margin-right: -1px; - font-size: 14px; - text-transform: uppercase; - cursor: pointer; - background: #F2F2F3; - font-family: $font-medium; - - &:first-child { - width: 156px; - } - - &:hover { - color: $primary-color; - background: #F9F9F9; - } - } - } - } // country-nav - - display: table; -} // country-header - -.highlight { - color: $primary-color; -} - -.country-section { - border-bottom: 1px solid #E5E5E5; - width: 100%; - padding: 45px 0; - - .section-info { - margin-bottom: 12px; - font-size: 14px; - color: $default-color; - font-family: $font-medium; - text-transform: uppercase; - letter-spacing: -0.2px; - - .info { - margin-right: 6px; - } - } - - .section-content { - padding-left: 30px; - @extend .clearfix; - - .left-col, - .right-col { - float: left; - } - - .left-col { - width: 40%; - } - - .right-col { - width: 55%; - padding-left: 5%; - } - - .section-title { - font-size: 29px; - line-height: 36px; - font-family: $font-medium; - color: $default-color; - } - } -} - -.country-alerts { - .forma-alerts-legend { - margin: 5px 0 25px 0; - - span { - color: #666666; - font-size: 13px; - font-family: $font-light; - display: block; - } - - .legend-title { - color: $primary-color; - font-size: 14px; - font-family: $font-medium; - margin-bottom: 3px; - } - } - - .btn { - border-radius: 20px; - border: none; - padding: 10px 20px 8px; - background: $primary-color; - font-family: $font-regular; - font-size: 13px; - } -} - -.country-forests-type { - .forests-type-graph { - margin-top: -25px; - } - - .forest-type-legends { - font-size: 14px; - display: inline-block; - margin-top: 10px; - - .legends-title { - color: $primary-color; - margin-bottom: 10px; - display: block; - } - - .legends-list { - li { - display: block; - margin-bottom: 5px; - color: #777; - font-family: $font-light; - vertical-align: top; - - span { - height: 14px; - width: 14px; - border-radius: 100px; - display: inline-block; - margin-right: 4px; - } - - .regenerated { - background: #819515; - } - - .primary { - background: #A1BA42; - } - - .planted { - background: #DDDDDD; - } - } - } - } -} - -.country-tenure { - .line-graph { - font-weight: bold; - text-transform: uppercase; - - circle { - stroke-width: 2px; - stroke: #f5f5f5; - } - - text { - @extend .sans-serif; - font-size: 12px; - } - - .one { - fill: #75B22E; - } - - .two { - fill: #AAC700; - } - - .three { - fill: #AC0; - } - - .four { - fill: #FFD24D; - } - } -} // country-tenure - -.country-employment { - .section-content .left-col { - width: 460px; - } - - .section-content .left-col .section-title { - @extend .clearfix; - margin-bottom: 10px; - font-size: 47px; - display: inline-block; - - div, - span { - margin-top: 8px; - float: left; - } - - span { - width: 250px; - margin: 4px 0 0 10px; - font-size: 11px; - line-height: 1.2; - @extend .sans-serif; - text-transform: uppercase; - color: #444; - } - - } - - .man-list { - color: #919191; - } -} - -/* =Overview ------------------------------------------------ */ - -.overview_graph { - margin: -106px auto 0; - height: 511px; - - @include background-image(linear-gradient(#464253, #383643)); -} - -.overview_graph__area { - height: 371px; - background: image-url('backgrounds/overview_graph.png'); - @include background-size(cover); - - svg { - position: relative; - z-index: 300; - margin-left: -40px; - } -} - -.tick { - fill: none; - shape-rendering: crispEdges; - stroke-width: 1px; - stroke-dasharray: 8, 4; -} - -.grid_h { - @extend .tick; - stroke: rgba(#fff, .1); -} - -.grid_v { - @extend .tick; - stroke: rgba(#fff, .3); -} - -.area { - @include opacity(.35); -} - -.linedot { - stroke-width: 2px; - stroke: #464253; - fill: #fff; - cursor: pointer; - - &.gain { - stroke: #98BD17; - } -} - -.overview_line { - @extend .tick; - stroke-width: 3px; - stroke: #98BD17; -} - -.minioverview_line { - @extend .tick; - stroke: #444; - - @include opacity(.5); -} - -.overview_graph__tabs { - @extend .clearfix; - - li { - display: table; - float: left; - - &:first-child a { - border-left: 0; - padding-left: 1px; - } - - &.selected, - &:hover { - a { - position: relative; - border-bottom: 0; - color: rgba(#fff, .75); - background: rgba(#000, .2); - } - } - - &.selected a:after { - content: ''; - position: absolute; - top: 0; left: 50%; - margin-left: -3px; - border: 6px solid transparent; - border-top-color: #fff; - } - - &.all_countries a { - background: #95BC3B; - margin-bottom: 0; - - &:hover { - background: darken(#95BC3B, 5%); - border-bottom: 1px solid #615D6C; - } - } - } -} - -.overview_graph__link { - display: table-cell; - height: 99px; - width: 159px; - border-left: 1px solid #615D6C; - border-bottom: 1px solid #615D6C; - font-size: 14px; - font-family: "fira_sans_otmedium"; - vertical-align: middle; - text-align: center; - text-transform: uppercase; - color: rgba(#fff, .75); - - span { - display: block; - font-size: 12px; - line-height: 1.5; - color: #A39FAA; - } -} - -.overview_graph__legend { - float: left; - width: 100%; - border-bottom: 1px solid #615D6C; - margin-top: -1px; - line-height: 40px; - font-size: 13px; - font-family: "fira_sans_otmedium"; - text-align: center; - background: rgba(#000, .2); - color: #fff; - - .info { - float: right; - margin-right: 20px; - } -} - -.overview_graph__years, -.overview_graph__axis, -.countries_list__header { - position: relative; - height: 28px; - border-bottom: 1px solid #CCC; - line-height: 28px; - font-size: 12px; - font-family: "fira_sans_otmedium"; - color: #9D9AA5; - text-transform: uppercase; - text-align: center; - - span { - text-transform: lowercase; - } -} - -.overview_graph__axis { - display: none; - text-transform: none; -} - -.year { - display: block; - position: absolute; - width: 30px; - margin-left: -15px; - text-align: center; -} - -.overview_graph__title { - border-bottom: 1px solid #ccc; - padding: 30px 0; - font-size: 37px; - line-height: 1.1; - font-family: "fira_sans_otregular"; - text-align: center; - color: #463F52; - vertical-align: top; - - sup { - position: relative; - top: -14px; - font-size: 23px; - } -} - -.countries_list ul li { - @extend .clearfix; - border-bottom: 1px solid #ccc; - padding: 18px 0; - - &:hover { - background: rgba(#ddd, .3); - - .bar { - @include opacity(1); - } - - .minioverview_line { - @include opacity(1); - } - - .countries_list__minioverview { - .loss { - color: $cGreen; - } - - .gain { - color: #C443FF; - } - } - } -} - -.countries_list__header__minioverview, -.countries_list__minioverview { - float: right; - width: 110px; - text-align: center; - - &.huge { - width: 330px; - } - - &.expanded { - width: 240px; - } - - &.medium { - width: 150px; - } -} - -.countries_list__header__num, -.countries_list__num, -.countries_list__header__title, -.countries_list__title { - float: left; -} - -.countries_list__header__num, -.countries_list__num { - width: 45px; - padding-left: 10px; - padding-right: 10px; -} - -.countries_list__header__title, -.countries_list__title { - padding-left: 15px; -} - -.countries_list__num, -.countries_list__title { - height: 30px; - padding-top: 10px; - padding-bottom: 10px; - font-size: 29px; - line-height: 30px; - font-family: "fira_sans_otmedium"; - color: #464152; -} - -.countries_list__num { - width: 45px; -} - -.countries_list__title { - border-left: 1px solid #ccc; -} - -.countries_list__minioverview { - height: 50px; - font-size: 29px; - line-height: 50px; - font-family: "fira_sans_otmedium"; - color: #A2A0A9; - - .loss { - color: rgba($cGreen, .5); - } - - .gain { - color: rgba(#C443FF, .5); - } - - .bar { - fill: #C443FF; - - &.extent { - fill: $cGreen; - } - } - - svg { - float: right; - margin: 10px; - } - - .sibling { - float: left; - border-right: 1px solid #ccc; - - svg { - padding-right: 10px; - } - } - - .half { - float: left; - width: 150px; - - &.last { - padding-left: 9px; - border-left: 1px solid #ccc; - margin-left: 10px; - } - } -} - -.countries_list__footer { - background: rgba(#98BD17, .15); - text-align: center; - padding: 30px 0; - - a { - display: inline-block; - border: 2px solid rgba(#464253, .25); - width: 192px; - padding: 0 20px; - font-size: 14px; - line-height: 38px; - font-family: "fira_sans_otmedium"; - text-align: left; - text-transform: uppercase; - color: #464152; - @include border-radius(21px); - - i { - float: right; - display: block; - height: 38px; - width: 10px; - @include country-icons-sprite(overview_footer); - } - - &:hover { - border: 2px solid rgba(#464253, .5); - } - } -} - -.tooltip { - position: absolute; - z-index: 500; - visibility: hidden; - padding: 20px 10px; - font-size: 19px; - font-family: "fira_sans_otmedium"; - text-align: center; - color: #C440FF; - background: #fff; - @include border-radius(5px); - @include box-shadow(0 0 3px #000); - - &:after { - content: ''; - position: absolute; - bottom: -6px; left: 50%; - border-color: #fff rgba(#fff, 0); - border-width: 6px 6px 0 6px; - border-style: solid; - margin-left: -6px; - } - - &.gain_tooltip, - &.tropical_tooltip { - color: #98BD17; - } - - &.subtropical_tooltip { - color: #FFFF73; - } - - &.boreal_tooltip { - color: #FFB973; - } - - &.temperature_tooltip { - color: #73DCFF; - } - - span { - display: block; - font-size: 15px; - line-height: 1.5; - color: rgba(#373343, .5); - } -} - -.chart { - display: none; - position: absolute; - width: 960px; - height: 371px; - color: #fff; - - &.total_loss { - display: block; - } - - &.percent_loss, - &.total_extent { - .disclaimer { - bottom: 40px; - } - } - - &.total_extent { - .legend { - top: 40px; - } - } - - .legend { - position: absolute; - z-index: 400; - top: 10px; left: 20px; - - li { - font-size: 19px; - line-height: 1.5; - font-family: "fira_sans_otregular"; - @include text-shadow(0 0 5px rgba(#000, .8)); - - &:first-child i { - background: #C441FF; - } - - &:last-child i { - background: #9ABF00; - } - - i { - display: inline-block; - position: relative; - top: -2px; - height: 6px; - width: 6px; - vertical-align: middle; - @include border-radius(4px); - } - } - } - - .disclaimer { - position: absolute; - z-index: 400; - left: 20px; bottom: 20px; - margin: 0; - font: normal 11px/1 "fira_sans_otmedium"; - color: #fff; - } -} - -.ball { - fill: #98BD17; - @include opacity(.8); -} - -.label { - font-size: 13px; - font-family: "fira_sans_otmedium"; - text-transform: uppercase; - fill: #fff; -} - -.axis { - font-size: 12px; - font-family: "fira_sans_otmedium"; - fill: #9D9AA5; -} - -.source_window .conventions .credits { - margin-bottom: 30px; -} - -.countries_list { - @extend .clearfix; -} - -.disclaimer { - margin-top: 40px; - font-style: italic; - line-height: 1.4; - @extend .serif; - color: #666; -} -.share_dialog { - $width: 463px; - $height: 170px; - - display: none; - position: fixed; - z-index: 2000; - top: 50%; left: 50%; - margin-top: -$height/2; - margin-left: -$width/2; - height: $height; - width: $width; - padding: 30px; - border: 1px solid #757573; - background: #fff; - @include box-shadow(0 0 7px #666); - @include border-radius(3px); - text-align: center; - - .form { - position: absolute; - bottom: 0; left: 0; - border-top: 1px solid #f1f1f1; - padding: 28px 10px 30px; - width: 498px; - } - - .error_input_label { - display: none; - position: absolute; - right: -92px; bottom: 37px; - width: 190px; - height: 39px - 9px; - padding: 9px 0 0; - color: #fff; - font-size: 15px; - @extend .sans-serif; - text-align: center; - @include country-icons-sprite(error_input_label); - } - - .btn { - width: 120px; - padding: 14px 0; - } - - .input-field { - position: relative; - width: 300px; - margin: 0 30px 0 0; - - &.error input { color: red;} - - .icon.error { - display: none; - width: 21px; - height: 22px; - position:absolute; - right: -7px; - top: 10px; - @include country-icons-sprite(input_error); - } - } - - .holder { - position: absolute; - top: 14px; left: 12px; - font-size: 15px; - @extend .sans-serif; - color: #ddd; - } - - .subtitle { - margin: 0 0 20px 0; - - a { - color: #A1BB27; - text-decoration: underline; - } - } - - .close { - display: block; - position: absolute; - top: 15px; right: 15px; - width: 6px; - height: 6px; - @include country-icons-sprite(infowindow_close); - } - - h1 { - font-size: 35px; - margin: 10px 0 5px; - @extend .serif; - } - - p { - font-size: 15px; - @extend .sans_serif; - color: #999; - - &.help { margin: 0 0 20px; } - } - .mode_menu { - position: absolute; - top: 4px; right: -15px; - width: 120px; - background: #fff; - - li.first a { - @include border-right-radius(3px); - } - - li.last a { - @include border-left-radius(3px); - } - - li.selected a { - background: $cGreen; - color: #fff; - } - - a { - display: block; - float: right; - width: 55px; - margin-left: 2px; - font-weight: bold; - font-size: 11px; - line-height: 34px; - text-transform: uppercase; - background: #eee; - } - } - .input-field { - @include inline-block(); - height:36px; - width: 416px; - - background: url(backgrounds/bkg_form_input.png) no-repeat left 0; - - input[type="text"], input[type="password"], input[type="email"],input[type="date"] { - width:100%; - height:37px; - margin: 0 0 0 7px; - padding: 0 7px 0 5px; - - font-size: 14px; - - background:url(backgrounds/bkg_form_input.png) repeat-x right -36px; - border:none; - outline:none; - } - &.huge { - height: 42px; - background:url(backgrounds/bkg_form_input_big.png) no-repeat left 0; - - input[type="password"], input[type="text"], input[type="email"] { - height:43px; - background:url(backgrounds/bkg_form_input_big.png) repeat-x right -42px; - } - } - } -} -.share_dialog { - height: 210px; - - .input-field { - width: 444px; - margin: 0 30px 0 20px; - } - - .share_buttons { - float: left; - width: 112px; - - a { - display: block; - float: left; - width: 32px; - height: 32px; - margin-right: 5px; - background: image-url('home-icons/ess-icons-2.png') no-repeat; - - &.twitter { background-position: -102px 0; } - &.facebook { background-position: -68px 0; } - - span { display: none; } - } - } } \ No newline at end of file diff --git a/app/assets/stylesheets/countries/index.scss b/app/assets/stylesheets/countries/index.scss new file mode 100644 index 0000000000..27b024b040 --- /dev/null +++ b/app/assets/stylesheets/countries/index.scss @@ -0,0 +1,97 @@ +.countries_block { + height: 120px; + margin-top: -10px; + background: + image-url('backgrounds/bkg_countries_block_up.png') no-repeat 0 center, + image-url('backgrounds/bkg_countries_block_down.png') repeat-x; + margin-bottom: 40px; + + .header-title { + font-size: 29px; + line-height: 120px; + color: #fff; + + a:hover { + text-decoration: underline; + } + } + span{ + color: darken($cGreen, 5%); + } +} + +.country { + float: left; + margin: 0 0 -1px -1px; + font-size: 15px; + line-height: 18px; + text-align: center; + color: #999; + + a { + position: relative; + display: block; + width: 199px; + height: 230px; + padding: 20px; + border: 1px solid #E5E5E5; + background: #fff; + + &:hover { + z-index: 1000; + margin: -10px; + padding: 30px; + @include box-shadow(0 0 5px rgba(#000, .1)); + + .country_main, + .country_alt { + fill: $cGreen; + } + + .country-content { + position: absolute; + top: 200px; left: 30px; right: 30px; + } + + svg { + top: 30px; + } + } + } + + .country-content { + position: absolute; + top: 190px; left: 20px; + right: 20px; + + span { + display: block; + color: #999; + font-size: 15px; + line-height: 1.1; + } + } + + h3 { + display: block; + margin-bottom: 5px; + font-size: 29px; + line-height: 1.1; + @extend .serif; + color: #666; + + &.small { + font-size: 19px; + } + } + + strong { + font-weight: bold; + } + + svg { + position: absolute; + top: 20px; left: 50%; + margin-left: -75px; + } +} \ No newline at end of file diff --git a/app/assets/stylesheets/countries/overview.scss b/app/assets/stylesheets/countries/overview.scss new file mode 100644 index 0000000000..013e97df17 --- /dev/null +++ b/app/assets/stylesheets/countries/overview.scss @@ -0,0 +1,697 @@ +.overview_graph { + margin: -106px auto 0; + height: 511px; + + @include background-image(linear-gradient(#464253, #383643)); +} + +.overview_graph__area { + height: 371px; + background: image-url('backgrounds/overview_graph.png'); + @include background-size(cover); + + svg { + position: relative; + z-index: 300; + margin-left: -40px; + } +} + +.tick { + fill: none; + shape-rendering: crispEdges; + stroke-width: 1px; + stroke-dasharray: 8, 4; +} + +.grid_h { + @extend .tick; + stroke: rgba(#fff, .1); +} + +.grid_v { + @extend .tick; + stroke: rgba(#fff, .3); +} + +.area { + @include opacity(.35); +} + +.linedot { + stroke-width: 2px; + stroke: #464253; + fill: #fff; + cursor: pointer; + + &.gain { + stroke: #98BD17; + } +} + +.overview_line { + @extend .tick; + stroke-width: 3px; + stroke: #98BD17; +} + +.minioverview_line { + @extend .tick; + stroke: #444; + + @include opacity(.5); +} + +.overview_graph__tabs { + @extend .clearfix; + + li { + display: table; + float: left; + + &:first-child a { + border-left: 0; + padding-left: 1px; + } + + &.selected, + &:hover { + a { + position: relative; + border-bottom: 0; + color: rgba(#fff, .75); + background: rgba(#000, .2); + } + } + + &.selected a:after { + content: ''; + position: absolute; + top: 0; left: 50%; + margin-left: -3px; + border: 6px solid transparent; + border-top-color: #fff; + } + + &.all_countries a { + background: #95BC3B; + margin-bottom: 0; + + &:hover { + background: darken(#95BC3B, 5%); + border-bottom: 1px solid #615D6C; + } + } + } +} + +.overview_graph__link { + display: table-cell; + height: 99px; + width: 159px; + border-left: 1px solid #615D6C; + border-bottom: 1px solid #615D6C; + font-size: 14px; + font-family: "fira_sans_otmedium"; + vertical-align: middle; + text-align: center; + text-transform: uppercase; + color: rgba(#fff, .75); + + span { + display: block; + font-size: 12px; + line-height: 1.5; + color: #A39FAA; + } +} + +.overview_graph__legend { + float: left; + width: 100%; + border-bottom: 1px solid #615D6C; + margin-top: -1px; + line-height: 40px; + font-size: 13px; + font-family: "fira_sans_otmedium"; + text-align: center; + background: rgba(#000, .2); + color: #fff; + + .info { + float: right; + margin-right: 20px; + } +} + +.overview_graph__years, +.overview_graph__axis, +.countries_list__header { + position: relative; + height: 28px; + border-bottom: 1px solid #CCC; + line-height: 28px; + font-size: 12px; + font-family: "fira_sans_otmedium"; + color: #9D9AA5; + text-transform: uppercase; + text-align: center; + + span { + text-transform: lowercase; + } +} + +.overview_graph__axis { + display: none; + text-transform: none; +} + +.year { + display: block; + position: absolute; + width: 30px; + margin-left: -15px; + text-align: center; +} + +.overview_graph__title { + border-bottom: 1px solid #ccc; + padding: 30px 0; + font-size: 37px; + line-height: 1.1; + font-family: "fira_sans_otregular"; + text-align: center; + color: #463F52; + vertical-align: top; + + sup { + position: relative; + top: -14px; + font-size: 23px; + } +} + +.countries_list ul li { + @extend .clearfix; + border-bottom: 1px solid #ccc; + padding: 18px 0; + + &:hover { + background: rgba(#ddd, .3); + + .bar { + @include opacity(1); + } + + .minioverview_line { + @include opacity(1); + } + + .countries_list__minioverview { + .loss { + color: $cGreen; + } + + .gain { + color: #C443FF; + } + } + } +} + +.countries_list__header__minioverview, +.countries_list__minioverview { + float: right; + width: 110px; + text-align: center; + + &.huge { + width: 330px; + } + + &.expanded { + width: 240px; + } + + &.medium { + width: 150px; + } +} + +.countries_list__header__num, +.countries_list__num, +.countries_list__header__title, +.countries_list__title { + float: left; +} + +.countries_list__header__num, +.countries_list__num { + width: 45px; + padding-left: 10px; + padding-right: 10px; +} + +.countries_list__header__title, +.countries_list__title { + padding-left: 15px; +} + +.countries_list__num, +.countries_list__title { + height: 30px; + padding-top: 10px; + padding-bottom: 10px; + font-size: 29px; + line-height: 30px; + font-family: "fira_sans_otmedium"; + color: #464152; +} + +.countries_list__num { + width: 45px; +} + +.countries_list__title { + border-left: 1px solid #ccc; +} + +.countries_list__minioverview { + height: 50px; + font-size: 29px; + line-height: 50px; + font-family: "fira_sans_otmedium"; + color: #A2A0A9; + + .loss { + color: rgba($cGreen, .5); + } + + .gain { + color: rgba(#C443FF, .5); + } + + .bar { + fill: #C443FF; + + &.extent { + fill: $cGreen; + } + } + + svg { + float: right; + margin: 10px; + } + + .sibling { + float: left; + border-right: 1px solid #ccc; + + svg { + padding-right: 10px; + } + } + + .half { + float: left; + width: 150px; + + &.last { + padding-left: 9px; + border-left: 1px solid #ccc; + margin-left: 10px; + } + } +} + +.countries_list__footer { + background: rgba(#98BD17, .15); + text-align: center; + padding: 30px 0; + + a { + display: inline-block; + border: 2px solid rgba(#464253, .25); + width: 192px; + padding: 0 20px; + font-size: 14px; + line-height: 38px; + font-family: "fira_sans_otmedium"; + text-align: left; + text-transform: uppercase; + color: #464152; + @include border-radius(21px); + + i { + float: right; + display: block; + height: 38px; + width: 10px; + @include country-icons-sprite(overview_footer); + } + + &:hover { + border: 2px solid rgba(#464253, .5); + } + } +} + +.tooltip { + position: absolute; + z-index: 500; + visibility: hidden; + padding: 20px 10px; + font-size: 19px; + font-family: "fira_sans_otmedium"; + text-align: center; + color: #C440FF; + background: #fff; + @include border-radius(5px); + @include box-shadow(0 0 3px #000); + + &:after { + content: ''; + position: absolute; + bottom: -6px; left: 50%; + border-color: #fff rgba(#fff, 0); + border-width: 6px 6px 0 6px; + border-style: solid; + margin-left: -6px; + } + + &.gain_tooltip, + &.tropical_tooltip { + color: #98BD17; + } + + &.subtropical_tooltip { + color: #FFFF73; + } + + &.boreal_tooltip { + color: #FFB973; + } + + &.temperature_tooltip { + color: #73DCFF; + } + + span { + display: block; + font-size: 15px; + line-height: 1.5; + color: rgba(#373343, .5); + } +} + +.chart { + display: none; + position: absolute; + width: 960px; + height: 371px; + color: #fff; + + &.total_loss { + display: block; + } + + &.percent_loss, + &.total_extent { + .disclaimer { + bottom: 40px; + } + } + + &.total_extent { + .legend { + top: 40px; + } + } + + .legend { + position: absolute; + z-index: 400; + top: 10px; left: 20px; + + li { + font-size: 19px; + line-height: 1.5; + font-family: "fira_sans_otregular"; + @include text-shadow(0 0 5px rgba(#000, .8)); + + &:first-child i { + background: #C441FF; + } + + &:last-child i { + background: #9ABF00; + } + + i { + display: inline-block; + position: relative; + top: -2px; + height: 6px; + width: 6px; + vertical-align: middle; + @include border-radius(4px); + } + } + } + + .disclaimer { + position: absolute; + z-index: 400; + left: 20px; bottom: 20px; + margin: 0; + font: normal 11px/1 "fira_sans_otmedium"; + color: #fff; + } +} + +.ball { + fill: #98BD17; + @include opacity(.8); +} + +.label { + font-size: 13px; + font-family: "fira_sans_otmedium"; + text-transform: uppercase; + fill: #fff; +} + +.axis { + font-size: 12px; + font-family: "fira_sans_otmedium"; + fill: #9D9AA5; +} + +.source_window .conventions .credits { + margin-bottom: 30px; +} + +.countries_list { + @extend .clearfix; +} + +.disclaimer { + margin-top: 40px; + font-style: italic; + line-height: 1.4; + @extend .serif; + color: #666; +} +.share_dialog { + $width: 463px; + $height: 170px; + + display: none; + position: fixed; + z-index: 2000; + top: 50%; left: 50%; + margin-top: -$height/2; + margin-left: -$width/2; + height: $height; + width: $width; + padding: 30px; + border: 1px solid #757573; + background: #fff; + @include box-shadow(0 0 7px #666); + @include border-radius(3px); + text-align: center; + + .form { + position: absolute; + bottom: 0; left: 0; + border-top: 1px solid #f1f1f1; + padding: 28px 10px 30px; + width: 498px; + } + + .error_input_label { + display: none; + position: absolute; + right: -92px; bottom: 37px; + width: 190px; + height: 39px - 9px; + padding: 9px 0 0; + color: #fff; + font-size: 15px; + @extend .sans-serif; + text-align: center; + @include country-icons-sprite(error_input_label); + } + + .btn { + width: 120px; + padding: 14px 0; + } + + .input-field { + position: relative; + width: 300px; + margin: 0 30px 0 0; + + &.error input { color: red;} + + .icon.error { + display: none; + width: 21px; + height: 22px; + position:absolute; + right: -7px; + top: 10px; + @include country-icons-sprite(input_error); + } + } + + .holder { + position: absolute; + top: 14px; left: 12px; + font-size: 15px; + @extend .sans-serif; + color: #ddd; + } + + .subtitle { + margin: 0 0 20px 0; + + a { + color: #A1BB27; + text-decoration: underline; + } + } + + .close { + display: block; + position: absolute; + top: 15px; right: 15px; + width: 6px; + height: 6px; + @include country-icons-sprite(infowindow_close); + } + + h1 { + font-size: 35px; + margin: 10px 0 5px; + @extend .serif; + } + + p { + font-size: 15px; + @extend .sans_serif; + color: #999; + + &.help { margin: 0 0 20px; } + } + .mode_menu { + position: absolute; + top: 4px; right: -15px; + width: 120px; + background: #fff; + + li.first a { + @include border-right-radius(3px); + } + + li.last a { + @include border-left-radius(3px); + } + + li.selected a { + background: $cGreen; + color: #fff; + } + + a { + display: block; + float: right; + width: 55px; + margin-left: 2px; + font-weight: bold; + font-size: 11px; + line-height: 34px; + text-transform: uppercase; + background: #eee; + } + } + .input-field { + @include inline-block(); + height:36px; + width: 416px; + + background: url(backgrounds/bkg_form_input.png) no-repeat left 0; + + input[type="text"], input[type="password"], input[type="email"],input[type="date"] { + width:100%; + height:37px; + margin: 0 0 0 7px; + padding: 0 7px 0 5px; + + font-size: 14px; + + background:url(backgrounds/bkg_form_input.png) repeat-x right -36px; + border:none; + outline:none; + } + &.huge { + height: 42px; + background:url(backgrounds/bkg_form_input_big.png) no-repeat left 0; + + input[type="password"], input[type="text"], input[type="email"] { + height:43px; + background:url(backgrounds/bkg_form_input_big.png) repeat-x right -42px; + } + } + } +} +.share_dialog { + height: 210px; + + .input-field { + width: 444px; + margin: 0 30px 0 20px; + } + + .share_buttons { + float: left; + width: 112px; + + a { + display: block; + float: left; + width: 32px; + height: 32px; + margin-right: 5px; + background: image-url('home-icons/ess-icons-2.png') no-repeat; + + &.twitter { background-position: -102px 0; } + &.facebook { background-position: -68px 0; } + + span { display: none; } + } + } +} \ No newline at end of file diff --git a/app/assets/stylesheets/countries/show.scss b/app/assets/stylesheets/countries/show.scss new file mode 100644 index 0000000000..6f89add429 --- /dev/null +++ b/app/assets/stylesheets/countries/show.scss @@ -0,0 +1,470 @@ +.country-show { + font-family: $font-regular; + color: $default-color; + + .country-header { + width: 100%; + + .country-header-inner { + height: 350px; + $wall-background: "backgrounds/wall_background_alpha.png"; + background: #464253; + background-image: image-url($wall-background); /* fallback */ + background-image: image-url($wall-background), -webkit-gradient(linear, left top, left bottom, from(#464253), to(#383643)); /* Saf4+, Chrome */ + background-image: image-url($wall-background), -webkit-linear-gradient(top, #464253, #383643); /* Chrome 10+, Saf5.1+ */ + background-image: image-url($wall-background), -moz-linear-gradient(top, #464253, #383643); /* FF3.6+ */ + background-image: image-url($wall-background), -ms-linear-gradient(top, #464253, #383643); /* IE10 */ + background-image: image-url($wall-background), -o-linear-gradient(top, #464253, #383643); /* Opera 11.10+ */ + background-image: image-url($wall-background), linear-gradient(to bottom, #464253, #383643); /* W3C */ + } + + .country-title { + padding: 20px; + + h1 { + color: white; + font-size: 47px; + max-width: 500px; + margin-right: 20px; + display: inline-block; + font-family: $font-regular; + } + + .country-selector { + display: inline-block; + vertical-align: top; + margin-top: 2px; + border: 2px solid #73707D; + border-radius: 40px; + height: 39px; + width: 310px; + } + + select { + border: none; + font-size: 13px; + text-transform: uppercase; + background: transparent; + color: #BEBCC2; + -webkit-appearance: none; + padding: 9px 15px; + width: 100%; + + &:focus { + outline: 0; + } + } + } // country-title + + .country-details { + background-color:rgba(0, 0, 0, 0.2); + height: 263px; + + .country-preview { + width: 735px; + height: 100%; + float: left; + @include box-sizing(border-box); + + h4 { + text-transform: uppercase; + color: white; + line-height: 18px; + font-size: 14px; + } + + .map, + .tree-numbers, + .loss-gain-graph { + float: left; + height: 100%; + @include box-sizing(border-box); + } + + .tree-numbers, + .loss-gain-graph { + padding: 30px 10px; + } + + .map { + width: 40%; + height: 100%; + } + + .tree-numbers { + width: 25%; + padding-left: 20px; + + h4 { + width: 100px; + } + + .amount, + .unit { + margin-top: 6px; + color: $primary-color; + display: inline-block; + } + + .amount { + font-size: 47px; + font-weight: bold; + } + + .total-extension { + margin-top: 18px; + } + + .total-extension .amount { + font-size: 29px; + } + } + + .loss-gain-graph { + width: 35%; + } + } + + .country-sidenav { + float: right; + width: 215px; + height: 100%; + background: #F2F2F3; + + .sidenav-icon { + display: inline-block; + float: right; + height: 18px; + width: 18px; + } + + > ul { + padding: 10px 0; + width: 100%; + + > li { + height: 53px; + width: 100%; + padding: 20px 25px; + @include box-sizing(border-box); + font-family: $font-medium; + text-transform: uppercase; + font-size: 14px; + cursor: pointer; + + a { + color: #464152; + } + + &:hover { + background: #FFFFFF; + } + } + } + } + + } // country-details + + .country-indepth { + clear: both; + border: 1px solid #CCCCCC; + border-bottom: 0; + height: 185px; + width: 100%; + padding: 35px 20px; + font-family: $font-medium; + @include box-sizing(border-box); + + //@include country-icons-sprite(country-indepth); + + .country-indepth__title { + font-size: 29px; + } + + .country-indepth__body { + text-transform: uppercase; + font-size: 14px; + font-weight: 700; + margin-top: 20px; + } + + .country-indepth__links { + margin-top: 25px; + color: #bbb; + font-family: $font-light; + font-size: 13px; + + a { + font-size: 14px; + display: inline-block; + vertical-align: top; + color: $primary-color; + font-family: $font-medium; + } + } + + } // country-indepth + } // country-header + + .country-nav-container { + width: 100%; + position: relative; + height: 49px; + + .country-nav { + width: 100%; + border-bottom: 1px solid #CCCCCC; + height: 49px; + position: absolute; + top: 0; + z-index: 100; + } + + .country-nav.fixed { + background: #F2F2F3; + .country-nav-items li a { + border-top: none; + } + } + + .country-nav-items { + width: 100%; + text-align: center; + + > li { + float: left; + margin-right: -1px; + height: 50px; + width: 135px; + @include box-sizing(border-box); + + &:first-child { + width: 156px; + } + + a { + border: 1px solid #ccc; + background: #F2F2F3; + width: 100%; + height: 100%; + display: inline-block; + padding: 11px 15px; + color: $default-color; + font-size: 14px; + text-transform: uppercase; + cursor: pointer; + font-family: $font-medium; + @include box-sizing(border-box); + + &.active { + background: white; + border-bottom: 1px solid white; + color: #999999; + } + } + + &:hover a { + color: $primary-color; + background: #F9F9F9; + + &.active { + background: white; + } + } + } + } // country-nav + } + + .highlight { + color: $primary-color; + } + + .country-section { + border-bottom: 1px solid #E5E5E5; + width: 100%; + padding: 45px 0; + + .section-info { + margin-bottom: 12px; + font-size: 14px; + color: $default-color; + font-family: $font-medium; + text-transform: uppercase; + letter-spacing: -0.2px; + + .info { + margin-right: 6px; + } + } + + .section-content { + padding-left: 30px; + @extend .clearfix; + + .left-col, + .right-col { + float: left; + } + + .left-col { + width: 40%; + } + + .right-col { + width: 55%; + padding-left: 5%; + } + + .section-title { + font-size: 29px; + line-height: 36px; + font-family: $font-medium; + color: $default-color; + } + } + } + + .country-alerts { + .forma-alerts-legend { + margin: 5px 0 25px 0; + + span { + color: #666666; + font-size: 13px; + font-family: $font-light; + display: block; + } + + .legend-title { + color: $primary-color; + font-size: 14px; + font-family: $font-medium; + margin-bottom: 3px; + } + } + + .btn { + border-radius: 20px; + border: none; + padding: 10px 20px 8px; + background: $primary-color; + font-family: $font-regular; + font-size: 13px; + } + } + + .country-forests-type { + .forests-type-graph { + margin-top: -25px; + } + + .forest-type-legends { + font-size: 14px; + display: inline-block; + margin-top: 10px; + + .legends-title { + color: $primary-color; + margin-bottom: 10px; + display: block; + } + + .legends-list { + li { + display: block; + margin-bottom: 5px; + color: #777; + font-family: $font-light; + vertical-align: top; + + span { + height: 14px; + width: 14px; + border-radius: 100px; + display: inline-block; + margin-right: 4px; + } + + .regenerated { + background: #819515; + } + + .primary { + background: #A1BA42; + } + + .planted { + background: #DDDDDD; + } + } + } + } + } + + .country-tenure { + .line-graph { + font-weight: bold; + text-transform: uppercase; + + circle { + stroke-width: 2px; + stroke: #f5f5f5; + } + + text { + @extend .sans-serif; + font-size: 12px; + } + + .one { + fill: #75B22E; + } + + .two { + fill: #AAC700; + } + + .three { + fill: #AC0; + } + + .four { + fill: #FFD24D; + } + } + } // country-tenure + + .country-employment { + .section-content .left-col { + width: 460px; + } + + .section-content .left-col .section-title { + @extend .clearfix; + margin-bottom: 10px; + font-size: 47px; + display: inline-block; + + div, + span { + margin-top: 8px; + float: left; + } + + span { + width: 250px; + margin: 4px 0 0 10px; + font-size: 11px; + line-height: 1.2; + @extend .sans-serif; + text-transform: uppercase; + color: #444; + } + + } + + .man-list { + color: #919191; + } + } +} \ No newline at end of file diff --git a/app/controllers/countries_controller.rb b/app/controllers/countries_controller.rb index 3d9b3277f8..3e150fbed1 100644 --- a/app/controllers/countries_controller.rb +++ b/app/controllers/countries_controller.rb @@ -23,6 +23,8 @@ def show_redesign @country['gva_percent'] = (@country['gva_percent'] < 0.1) ? number_to_percentage(@country['gva_percent'], precision: 2) : number_to_percentage(@country['gva_percent'], precision: 1) @employees = @country['employment'] @conventions = %w(cbd unfccc kyoto unccd itta cites ramsar world_heritage nlbi ilo) + + end private diff --git a/app/views/countries/show_redesign.html.erb b/app/views/countries/show_redesign.html.erb index 673d1d90b1..1c563a41ec 100644 --- a/app/views/countries/show_redesign.html.erb +++ b/app/views/countries/show_redesign.html.erb @@ -10,6 +10,10 @@ <% end %> +<% content_for :css do %> + +<% end %> +
    @@ -19,15 +23,15 @@

    <%= @country['name'] %>

    - +
    -
    +

    Tree cover

    @@ -52,17 +56,23 @@
    - <% if !@country['indepth'].present? %> + <% if @country['indepth'].present? %>

    Global Forest Watch does in-depth work in this country.

    Find more information at the Atlas Foreiester Interactif de la République Democratique Du Congo

    @@ -72,22 +82,23 @@
    <% end %> +
    +
    - -
    - + + @@ -213,7 +224,7 @@ -
    +
    -
    +
    -
    +
    +
    +
    + + <% end %> + + +
    +
    + + +
    +
    +
    +
    +
    -
    -

    Forest policy and legislation

    -
      - <% if @country['national_policy_link'].present? %> -
    • <%= link_to @country['national_policy_title'].present? ? @country['national_policy_title'] : 'National Forest Policy', @country['national_policy_link'] %>
    • - <% end %> -
    + +
    +
    + + + +
    +
    - Are we missing a link? -
    - + +
    +
    -
    -
    -
      + <% if @country['carbon_stocks'].present? && @country['carbon_stocks'] != 0 %> -
    • '> -

      Carbon stocks

      -

      This country has <%= number_with_delimiter(@country['carbon_stocks']) %> million
      metric tons of carbon stocks
      in living forest biomass.

      -
    • +
      + + +
      +

      This country has <%= number_with_delimiter(@country['carbon_stocks']) %> million metric tons of carbon stocks in living forest biomass.

      +
      +
      <% end %> + <% if @country['emissions'].present? && @country['emissions'] != 0 %> -
    • last'> -

      GHG emissions

      +
      - <% precission = (@country['emissions'].abs.to_i < 0.1) ? 2 : 1 %> + +
      <% if @country['emissions'] > 0 %> -

      According to FAO data, <%= number_to_percentage(@country['emissions'], precision: precission) %> of GHG emissions in this country came from land-use change and forestry in 2010.

      +

      According to FAO data, <%= number_to_percentage(@country['emissions'], precision: @precission) %> of GHG emissions in this country came from land-use change and forestry in 2010.

      <% else %> -

      According to FAO data, land-use change and forestry sequestered <%= number_to_percentage(@country['emissions'].abs, precision: precission) %> of this country’s GHG emissions in 2010.

      +

      According to FAO data, land-use change and forestry sequestered <%= number_to_percentage(@country['emissions'].abs, precision: @precission) %> of this country’s GHG emissions in 2010.

      <% end %> -
    • +
    +
    <% end %> - -
    - - -
    -
    -

    Conventions

    - - <% conventions = ['cbd', 'unfccc', 'kyoto', 'unccd', 'itta', 'cites', 'ramsar', 'world_heritage', 'nlbi', 'ilo'] %> -
      - <% conventions.each do |convention| %> - <% if @country["convention_#{convention}"].present? %> -
    • <%= t('.conventions.'+convention+'_title_html') %> - <%= @country["convention_#{convention}"] %> -
    • - <% end %> - <% end %> -
    -
    -
    + + -<% if @country['ministry_link'].present? || @country['external_links'].present? %> - -<% end %> + <% end %> - - -
    -
    -

    <%= t('.country_blog.title', :country => @country['name'].titleize) %>

    - -
      -
    • - <% unless @blog_story.nil? %> - Blog stories -

      <%= truncate(@blog_story['title'], :length => 35) %>

      -

      <%= truncate(@blog_story['description'].gsub('[…]', ''), :length => 300) %> <%= link_to('more', @blog_story['link'], :target => '_blank') %>

      -

      Read more blog stories '>here

      - <% end %> -
    • - -
    • '> - User stories - <% if @country['story'].present? %> - - " alt="<%= @country['story']['title'] %>" /> -
      -
      -
      -
      - <%= @country['story']['title'] %> - <%= t('.home_stories.featured.read_more') %> -
      -
      + +
    • - -
    • - <% if @mongabay_story.present? %> - Mongabay stories -

      <%= truncate(@mongabay_story['title'], :length => 35) %>

      -

      <%= truncate(@mongabay_story['description'], :length => 300) %> <%= link_to('more', @mongabay_story['loc'], :target => '_blank') %>

      -

      See more on the <%= link_to 'map', "#{map_path}/3/15.00/27.00/ALL/586" %>

      + <%= link_to 'Download relevant data sets','#', class: 'btn-rdn disabled' %> <% end %> -
    • -
    -
    -
    - -
    -
    -
    - - +
    + +
    + + +
    +
    +
    +

    <%= t('.country_blog.title', :country => @country['name'].titleize) %>

    + + + +
    +
    +
    + + +
    +
    +
    + + +
    +
    + +
    + <%= render 'shared/countries' %> + <%= render 'shared/sources' %>
    - -
    - <%= render 'shared/countries' %> - <%= render 'shared/sources' %>
    diff --git a/app/views/countries/show_redesign.html.erb b/app/views/countries/show_redesign.html.erb deleted file mode 100644 index 1c563a41ec..0000000000 --- a/app/views/countries/show_redesign.html.erb +++ /dev/null @@ -1,399 +0,0 @@ -<% content_for :js do %> - -<% end %> - -<% content_for :css do %> - -<% end %> - -
    -
    -
    - - -
    -
    -

    <%= @country['name'] %>

    -
    - -
    -
    - -
    -
    -
    -
    -
    -

    Tree cover

    - - <%= number_to_human(@country['extent'])[0,3] %> - mha -
    -
    -

    Total extension

    - 851 - mha -
    -
    -
    -

    LOSS AND GAIN (2001 - 2012)

    -
    -
    -
    -
    -
    -
    - -
    -
    -
    - - - <% if @country['indepth'].present? %> -
    -

    Global Forest Watch does in-depth work in this country.

    -

    Find more information at the Atlas Foreiester Interactif de la République Democratique Du Congo

    - -
    - <% end %> -
    -
    - - - - - -
    -
    - - -
    -
    -

    There were 7,363 FORMA alerts in october

    - -
    - Forest Clearing Alerts - Humid Tropics -
    - - Download data -
    -
    -
    -
    -
    -
    -
    - - -
    -
    - - -
    -
    -

    - <%= @country['name'] %> has <%= number_to_human(@country['extent'], precision: 2).downcase %> hectares of tree cover -

    - -
    - Forest Type: -
      -
    • Regenerated
    • -
    • Primary
    • -
    • Planted
    • -
    -
    -
    -
    -
    -
    -
    -
    -
    - - -
    -
    - - -
    -

    - The forestry sector contributed USD <%= gva_to_human(@country['gva'])%> to the economy in 2006, which is appoximately <%= @country['gva_percent'] %> of the GDP. -

    -
    -
    -
    - - - <% if @employees.present? && @employees > 0 %> -
    -
    - - -
    -
    - <% if @employees < 1000 %> -

    <%= @employees %>
    thousand people are directly employed by the forestry sector, according to 2006 FAO data.

    - <% else %> -

    <%= (@employees/1000.00).round(2) %>
    million people are directly employed by the forestry sector, according to 2006 FAO data.

    - <% end %> - -
      '> -
    • - <% @employees = @employees < 100 ? @employees : 100 %> -
      - <% @employees.times do |i| %> - <%= image_tag 'countries/man.png' %> - <% end %> - - <%= '...' if @employees == 100 %> -
      -
    • -
    -
    -
    -
    -
    - <% end %> - - -
    -
    - - -
    -
    -
    -
    -
    - - -
    -
    - - - -
    -
    - - -
    -
    - - -
    -
    -

    This country has <%= number_with_delimiter(@country['carbon_stocks']) %> million
    metric tons of carbon stocks
    in living forest biomass.

    -
    -
    - -
    -
    -
    -
    - - -
    -
    - - -
    -
      - <% @conventions.each do |convention| %> - <% if @country["convention_#{convention}"].present? %> -
    • <%= t('.conventions.'+convention+'_title_html') %> - <%= @country["convention_#{convention}"] %> -
    • - <% end %> - <% end %> -
    -
    -
    -
    - - - <% if @country['ministry_link'].present? || @country['external_links'].present? %> - - <% end %> - - - - - -
    -
    -

    <%= t('.country_blog.title', :country => @country['name'].titleize) %>

    - - -
    -
    - - -
    -
    -
    - - -
    -
    - -
    - <%= render 'shared/countries' %> - <%= render 'shared/sources' %> -
    - -
    diff --git a/config/routes.rb b/config/routes.rb index a0f4de6a05..61d2f67102 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -26,9 +26,6 @@ get '/countries' => 'countries#index' get '/country/:id' => 'countries#show', :as => 'country' get '/countries/overview' => 'countries#overview' - # New country page - get '/r/country/:id' => 'countries#show_redesign', :as =>'country_redesign' - # media post 'media/upload' => 'media#upload' From 0ca42370a379097020e56086a352edfe83adff9e Mon Sep 17 00:00:00 2001 From: Pedro Garcia Date: Mon, 26 May 2014 19:06:30 +0200 Subject: [PATCH 012/823] changes --- .../javascripts/countries/views/show.js | 110 +++++++++++++++++- app/assets/stylesheets/countries/show.scss | 17 ++- app/controllers/countries_controller.rb | 28 ++--- app/views/countries/show.html.erb | 24 +++- 4 files changed, 156 insertions(+), 23 deletions(-) diff --git a/app/assets/javascripts/countries/views/show.js b/app/assets/javascripts/countries/views/show.js index 49f82f06af..34a50c608e 100644 --- a/app/assets/javascripts/countries/views/show.js +++ b/app/assets/javascripts/countries/views/show.js @@ -3,6 +3,7 @@ gfw.ui.view.CountriesShow = cdb.core.View.extend({ events: { 'click .info': '_openSource', + 'click .forma_dropdown-link': '_openDropdown', 'change #areaSelector': '_onSelectArea' }, @@ -34,9 +35,11 @@ gfw.ui.view.CountriesShow = cdb.core.View.extend({ _initViews: function() { this._initSource(); + this._drawLossAndGain(); this._drawTenure(); this._drawForestsType(); this._drawFormaAlerts(); + this._initFormaDropdown(); }, _initSource: function() { @@ -53,6 +56,35 @@ gfw.ui.view.CountriesShow = cdb.core.View.extend({ this.sourceWindow.show(source).addScroll(); }, + _initFormaDropdown: function() { + $('.forma_dropdown-link').qtip({ + show: 'click', + hide: { + event: 'click unfocus' + }, + content: { + text: $('.forma_dropdown-menu') + }, + position: { + my: 'bottom right', + at: 'top right', + target: $('.forma_dropdown-link'), + adjust: { + x: -10 + } + }, + style: { + tip: { + corner: 'bottom right', + mimic: 'bottom center', + border: 1, + width: 10, + height: 6 + } + } + }); + }, + _setAreaSelector: function() { var self = this; @@ -307,6 +339,76 @@ gfw.ui.view.CountriesShow = cdb.core.View.extend({ }); }, + _drawLossAndGain: function() { + var sql = "SELECT year, loss_gt_0 loss, extent_gt_25 extent FROM umd WHERE iso='" + this.country.get('iso') + "'"; + + // if (options.dataset === 'loss') { + // sql += "year, loss_gt_0 loss FROM umd WHERE iso='" + this.country.get('iso') + "'"; + // } else if (options.dataset === 'extent') { + // sql += "year, extent_gt_25 extent FROM umd WHERE iso='" + this.country.get('iso') + "'"; + // } + + d3.json('https://wri-01.cartodb.com/api/v2/sql?q=' + sql, function(json) { + if (json) { + $graph.removeClass('ghost'); + + var data = json.rows; + } else { + $coming_soon.show(); + + return; + } + + var data_ = []; + + _.each(data, function(val, key) { + if (val.year >= 2001) { + data_.push({ + 'year': val.year, + 'value': eval('val.'+options.dataset) + }); + } + }); + + $amount.html(''+formatNumber(parseInt(data_[data_.length - 1].value, 10))+''); + $date.html('Hectares in ' + data_[data_.length - 1].year); + + var marginLeft = 40, + marginTop = radius - h/2 + 5; + + var y_scale = d3.scale.linear() + .domain([0, d3.max(data_, function(d) { return parseFloat(d.value); })]) + .range([height, marginTop*2]); + + var barWidth = (width - 80) / data_.length; + + var bar = graph.selectAll('g') + .data(data_) + .enter() + .append('g') + .attr('transform', function(d, i) { return 'translate(' + (marginLeft + i * barWidth) + ','+ -marginTop+')'; }); + + bar.append('svg:rect') + .attr('class', function(d, i) { + if (i === 11) { // last year index + return 'last bar' + } else { + return 'bar' + } + }) + .attr('y', function(d) { return y_scale(d.value); }) + .attr('height', function(d) { return height - y_scale(d.value); }) + .attr('width', barWidth - 1) + .on('mouseover', function(d) { + d3.selectAll('.bar').style('opacity', '.5'); + d3.select(this).style('opacity', '1'); + + $amount.html(''+formatNumber(parseInt(d.value, 10))+''); + $date.html('Hectares in ' + d.year); + }); + }); + }, + _drawForestsType: function() { var sql = ["SELECT unnest(array['forest_regenerated', 'forest_primary', 'forest_planted'])", 'AS type, unnest(array[COALESCE(forest_regenerated, 0),', @@ -463,6 +565,10 @@ gfw.ui.view.CountriesShow = cdb.core.View.extend({ .attr('cy', cy) .attr('r', 5); }); - } + }, + + _openDropdown: function(e) { + e.preventDefault(); + }, -}); +}); \ No newline at end of file diff --git a/app/assets/stylesheets/countries/show.scss b/app/assets/stylesheets/countries/show.scss index f5cb34a018..e5ecf09927 100644 --- a/app/assets/stylesheets/countries/show.scss +++ b/app/assets/stylesheets/countries/show.scss @@ -157,7 +157,7 @@ cursor: pointer; } - .sidenav-icon { + a .sidenav-icon { display: inline-block; position: absolute; top: 17px; right: 20px; @@ -331,9 +331,24 @@ color: $default-color; } } + + .country-alt { + display: inline-block; + width: 100%; + clear: both; + padding-top: 20px; + color: #666; + line-height: 1.4; + font-size: 14px; + font-style: italic; + } } .country-alerts { + .forma_dropdown-menu { + display: none; + } + .forma-alerts-legend { margin: 5px 0 25px 0; diff --git a/app/controllers/countries_controller.rb b/app/controllers/countries_controller.rb index 8f61221535..7aac477366 100644 --- a/app/controllers/countries_controller.rb +++ b/app/controllers/countries_controller.rb @@ -1,29 +1,23 @@ class CountriesController < ApplicationController - before_filter :load_countries, :only => [:index, :show_redesign] + before_filter :load_countries, :only => [:index] include ActionView::Helpers::NumberHelper - # def show - # country = Api::Country.find_by_iso(params[:id])['countries'][0] - - # not_found unless country.present? - - # blog_story = Api::Blog.find_by_country(country) - # @blog_story = blog_story.present? ? blog_story : nil - - # response = Typhoeus.get("https://wri-01.cartodb.com/api/v2/sql?q=SELECT%20*%20FROM%20mongabaydb%20WHERE%20position('#{I18n.transliterate(country['name']).downcase.gsub(" ", "_")}'%20in%20keywords)%20%3C%3E%200", headers: { "Accept" => "application/json" }) - # @mongabay_story = response.success? ? JSON.parse(response.body)['rows'][0] : nil - - # @country = country - # end - def show @country = Api::Country.find_by_iso(params[:id])['countries'][0] not_found unless @country.present? - @country['gva_percent'] = (@country['gva_percent'] < 0.1) ? number_to_percentage(@country['gva_percent'], precision: 2) : number_to_percentage(@country['gva_percent'], precision: 1) + if @country['gva'].present? && @country['gva'] > 0 + @country['gva_percent'] = (@country['gva_percent'] < 0.1) ? number_to_percentage(@country['gva_percent'], precision: 2) : number_to_percentage(@country['gva_percent'], precision: 1) + end + @employees = @country['employment'] @conventions = %w(cbd unfccc kyoto unccd itta cites ramsar world_heritage nlbi ilo) - @precission = (@country['emissions'].abs.to_i < 0.1) ? 2 : 1 + + blog_story = Api::Blog.find_by_country(@country) + @blog_story = blog_story.present? ? blog_story : nil + + response = Typhoeus.get("https://wri-01.cartodb.com/api/v2/sql?q=SELECT%20*%20FROM%20mongabaydb%20WHERE%20position('#{I18n.transliterate(@country['name']).downcase.gsub(" ", "_")}'%20in%20keywords)%20%3C%3E%200", headers: { "Accept" => "application/json" }) + @mongabay_story = response.success? ? JSON.parse(response.body)['rows'][0] : nil end private diff --git a/app/views/countries/show.html.erb b/app/views/countries/show.html.erb index 270c041033..62ca5770f0 100644 --- a/app/views/countries/show.html.erb +++ b/app/views/countries/show.html.erb @@ -124,8 +124,18 @@ Humid Tropics - Download data +
    + +
    + Download data +
    @@ -159,6 +169,12 @@
    + + <% if @country['country_alt'].present? %> +
    +

    <%= @country['country_alt'].html_safe %>

    +
    + <% end %> @@ -269,11 +285,13 @@ + <% precission = (@country['emissions'].abs.to_i < 0.1) ? 2 : 1 %> +
    <% if @country['emissions'] > 0 %> -

    According to FAO data, <%= number_to_percentage(@country['emissions'], precision: @precission) %> of GHG emissions in this country came from land-use change and forestry in 2010.

    +

    According to FAO data, <%= number_to_percentage(@country['emissions'], precision: precission) %> of GHG emissions in this country came from land-use change and forestry in 2010.

    <% else %> -

    According to FAO data, land-use change and forestry sequestered <%= number_to_percentage(@country['emissions'].abs, precision: @precission) %> of this country’s GHG emissions in 2010.

    +

    According to FAO data, land-use change and forestry sequestered <%= number_to_percentage(@country['emissions'].abs, precision: precission) %> of this country’s GHG emissions in 2010.

    <% end %>
    From 70c65218f03616c3a61545c8056261be2669e51f Mon Sep 17 00:00:00 2001 From: rschumann Date: Tue, 27 May 2014 10:04:49 +0200 Subject: [PATCH 013/823] changes for countries --- app/controllers/countries_controller.rb | 28 +++++++++++++++++++++---- app/controllers/embed_controller.rb | 13 +++++++++++- app/controllers/home_controller.rb | 15 +++++++++---- app/controllers/static_controller.rb | 2 +- app/views/home/index.html.erb | 3 +-- app/views/shared/_footer.html.erb | 2 +- lib/api/blog.rb | 2 +- lib/api/country.rb | 23 -------------------- spec/features/countries_page_spec.rb | 10 +++++++++ spec/features/stories_page_spec.rb | 5 ++++- spec/spec_helper.rb | 3 +++ 11 files changed, 68 insertions(+), 38 deletions(-) delete mode 100644 lib/api/country.rb diff --git a/app/controllers/countries_controller.rb b/app/controllers/countries_controller.rb index 30fb1d6727..e6da010120 100644 --- a/app/controllers/countries_controller.rb +++ b/app/controllers/countries_controller.rb @@ -1,9 +1,12 @@ class CountriesController < ApplicationController - before_filter :load_countries, :only => [:index] + + def index + @countries = find_countries + end # GET /country/:id def show - country = Api::Country.find_by_iso(params[:id])['countries'][0] + country = find_by_iso(params[:id]) not_found unless country.present? @@ -17,9 +20,26 @@ def show end private + def find_countries + response = Typhoeus.get("#{ENV['GFW_API_HOST']}/countries", headers: {"Accept" => "application/json"}) + if response.success? + JSON.parse(response.body)['countries'] + else + nil + end + end - def load_countries - @countries = Api::Country.all['countries'] + def find_by_iso(iso) + response = Typhoeus.get( + "#{ENV['GFW_API_HOST']}/countries", + headers: {"Accept" => "application/json"}, + params: {iso: iso} + ) + if response.success? + JSON.parse(response.body)['countries'][0] + else + nil + end end end diff --git a/app/controllers/embed_controller.rb b/app/controllers/embed_controller.rb index 00ca16a1af..966281ed69 100644 --- a/app/controllers/embed_controller.rb +++ b/app/controllers/embed_controller.rb @@ -3,11 +3,22 @@ class EmbedController < ApplicationController # GET /embed/country/:id def countries_show - country = Api::Country.find_by_iso(params[:id])['countries'][0] + country = find_by_iso(params[:id]) not_found unless country.present? @country = country end + private + def find_by_iso(iso) + response = Typhoeus.get("#{ENV['GFW_API_HOST']}/countries", params: { iso: iso }, headers: {"Accept" => "application/json"}) + + if response.success? + JSON.parse(response.body)['countries'][0] + else + nil + end + end + end diff --git a/app/controllers/home_controller.rb b/app/controllers/home_controller.rb index c6c9660381..6d268ccf25 100644 --- a/app/controllers/home_controller.rb +++ b/app/controllers/home_controller.rb @@ -1,6 +1,6 @@ class HomeController < ApplicationController - skip_before_filter :check_terms, :only => [:accept_and_redirect] + skip_before_filter :check_terms, :only => [:accept_and_redirect] before_filter :load_circles, :validate_url, :only => [:index] def index @@ -31,9 +31,16 @@ def validate_url def load_circles begin - Rails.cache.fetch "circles", expires_in: 1.day do - response = Typhoeus.get("https://wri-01.cartodb.com/api/v2/sql?q=WITH%20loss%20as%20(SELECT%20sum(loss_gt_0)%20as%20sum_loss%2C%20(SELECT%20sum(loss_gt_0)%20FROM%20umd%20WHERE%20year%20%3D%202012)%20as%20loss_2012%20FROM%20umd)%2C%20gain%20as%20(SELECT%20sum(umd.gain)%20last_gain%20FROM%20(SELECT%20DISTINCT%20iso%2C%20gain%20FROM%20umd)%20umd)%2C%20forma%20as%20(SELECT%20count(cartodb_id)%20as%20gain%20FROM%20forma_api%20WHERE%20date%20%3E%3D%20(SELECT%20max(date)%20FROM%20forma_api))%2C%20circles%20as%20(SELECT%20unnest(array%5B%27sum_loss%27%2C%20%27loss_2012%27%2C%20%27last_gain%27%2C%20%27gain%27%5D)%20AS%20slug%2C%20unnest(array%5Bsum_loss%2C%20loss_2012%2C%20last_gain%2C%20gain%5D)%20AS%20num%20FROM%20loss%2C%20gain%2C%20forma)%0A%0ASELECT%20*%20FROM%20carrousel%20LEFT%20OUTER%20JOIN%20circles%20ON%20(circles.slug%20%3D%20carrousel.slug)%20WHERE%20published%20IS%20true%20ORDER%20BY%20pos%20ASC", headers: {"Accept" => "application/json"}) - response.success? ? JSON.parse(response.body)['rows'] : nil + response = Typhoeus.get( + "https://wri-01.cartodb.com/api/v2/sql?q=WITH%20loss%20as%20(SELECT%20sum(loss_gt_0)%20as%20sum_loss%2C%20(SELECT%20sum(loss_gt_0)%20FROM%20umd%20WHERE%20year%20%3D%202012)%20as%20loss_2012%20FROM%20umd)%2C%20gain%20as%20(SELECT%20sum(umd.gain)%20last_gain%20FROM%20(SELECT%20DISTINCT%20iso%2C%20gain%20FROM%20umd)%20umd)%2C%20forma%20as%20(SELECT%20count(cartodb_id)%20as%20gain%20FROM%20forma_api%20WHERE%20date%20%3E%3D%20(SELECT%20max(date)%20FROM%20forma_api))%2C%20circles%20as%20(SELECT%20unnest(array%5B%27sum_loss%27%2C%20%27loss_2012%27%2C%20%27last_gain%27%2C%20%27gain%27%5D)%20AS%20slug%2C%20unnest(array%5Bsum_loss%2C%20loss_2012%2C%20last_gain%2C%20gain%5D)%20AS%20num%20FROM%20loss%2C%20gain%2C%20forma)%0A%0ASELECT%20*%20FROM%20carrousel%20LEFT%20OUTER%20JOIN%20circles%20ON%20(circles.slug%20%3D%20carrousel.slug)%20WHERE%20published%20IS%20true%20ORDER%20BY%20pos%20ASC", + headers: {"Accept" => "application/json"} + ) + if response.success? + Rails.cache.fetch 'circles', expires_in: 1.day do + JSON.parse(response.body)['rows'] + end + else + nil end rescue Exception => e Rails.logger.error "Error retrieving circles in the Home: #{e}" diff --git a/app/controllers/static_controller.rb b/app/controllers/static_controller.rb index 4ecfdda530..7ac1b2b0fb 100644 --- a/app/controllers/static_controller.rb +++ b/app/controllers/static_controller.rb @@ -1,7 +1,7 @@ class StaticController < ApplicationController skip_before_filter :check_terms, :except => [:data] - layout 'old', :only => [:old, :terms, :accept_terms] + layout 'old', :only => :accept_terms def accept_terms session[:return_to] = params[:return_to] unless params[:return_to].nil? diff --git a/app/views/home/index.html.erb b/app/views/home/index.html.erb index 7079bdc21a..a4f0c6f6e1 100644 --- a/app/views/home/index.html.erb +++ b/app/views/home/index.html.erb @@ -69,8 +69,7 @@
      <% @visible.each do |story| %> -
    • +
    • " alt="<%= story['title'] %>" />
      diff --git a/app/views/shared/_footer.html.erb b/app/views/shared/_footer.html.erb index 2d22aea3f1..5d014e6016 100644 --- a/app/views/shared/_footer.html.erb +++ b/app/views/shared/_footer.html.erb @@ -11,7 +11,7 @@
      - +

      Tweet to @globalforests

    • Info - +
    • Download data - +
    • Analyze - +
    From 787a363d54365b7bc059c2c34f6e6e42d2f68b14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A1n=20P=C3=A9rez?= Date: Tue, 27 May 2014 15:51:32 +0200 Subject: [PATCH 015/823] umd layer: canopy intensity dialog finished --- app/assets/stylesheets/home.scss | 53 ++++++++- app/views/shared/_js_templates.html.erb | 24 +++-- .../javascripts/gfw/ui/umd_options.js.erb | 102 ++++++++++++++++-- 3 files changed, 157 insertions(+), 22 deletions(-) diff --git a/app/assets/stylesheets/home.scss b/app/assets/stylesheets/home.scss index 16892e335c..9b7692f586 100644 --- a/app/assets/stylesheets/home.scss +++ b/app/assets/stylesheets/home.scss @@ -1570,6 +1570,8 @@ body { .source { top: 4px; right: 7px; + width: 10px; + padding: 0; } } &.trees { @@ -1605,6 +1607,7 @@ body { background: image-url('/assets/icons/point.png') repeat-x -2px center; position: relative; top: 21px; + cursor: pointer; &:focus { outline: 0; } @@ -1615,6 +1618,9 @@ body { height: 14px; @include home-icons-sprite(timeline_handler); cursor: col-resize; + border-radius: 3px; + z-index: 500; + position:relative; } } /*#canopy_slider::-webkit-slider-thumb/*, @@ -1637,6 +1643,46 @@ body { box-shadow: 1px 0 3px #cccccc; right: 0; width: 60px; + border-top-right-radius: 3px; + border-bottom-right-radius: 3px; + pointer-events:none; + } + a { + @include inline-block(); + width: 70px; + padding: 10px 0; + border: #666666; + @include border-radius(3px); + font-weight:bold; + + text-align: center; + font-size:11px; + text-transform:uppercase; + text-decoration:none; + + &.apply { + background:#A1BA42; + color: #fff; + border: 1px solid #839C26; + + &:hover { + background: darken(#A1BA42, 10%); + border-color:darken(#A1BA42, 10%); + } + } + + &.cancel { + background: #fff; + border: 1px solid #CCCCCC; + color: #666666; + + &:hover { background:#f1f1f1; } + + } + } + &:last-child { + float: right; + margin-bottom: 0; } } ul { @@ -1649,9 +1695,13 @@ body { color: #A2BE00; font-size: 10px; position: absolute; - &.p0 { + cursor: pointer; + z-index: 5; + &.p0, + &.p100 { color: #666; left: 0; + cursor: default; } &.p10 { left: calc(12% - 5px); @@ -1675,7 +1725,6 @@ body { left: calc(12*7% - 5px); } &.p100 { - color: #666; left: calc(12*8% - 5px); } } diff --git a/app/views/shared/_js_templates.html.erb b/app/views/shared/_js_templates.html.erb index 0de60613c8..4242d2f675 100644 --- a/app/views/shared/_js_templates.html.erb +++ b/app/views/shared/_js_templates.html.erb @@ -640,19 +640,23 @@

    Change canopy treshold.

      -
    • 0%
    • -
    • 10%
    • -
    • 15%
    • -
    • 20%
    • -
    • 25%
    • -
    • 30%
    • -
    • 50%
    • -
    • 75%
    • -
    • 100%
    • +
    • 0%
    • +
    • 10%
    • +
    • 15%
    • +
    • 20%
    • +
    • 25%
    • +
    • 30%
    • +
    • 50%
    • +
    • 75%
    • +
    • 100%

    - +

    +

    + Cancel + Apply +

    \ No newline at end of file diff --git a/lib/assets/javascripts/gfw/ui/umd_options.js.erb b/lib/assets/javascripts/gfw/ui/umd_options.js.erb index 2d15513f33..4cefff08dd 100644 --- a/lib/assets/javascripts/gfw/ui/umd_options.js.erb +++ b/lib/assets/javascripts/gfw/ui/umd_options.js.erb @@ -81,7 +81,12 @@ gfw.ui.view.umdoptionsDialog = gfw.ui.view.Widget.extend({ className: 'umdoptions_dialog', events: { - 'click .close': 'hide' + 'click .close': 'hide', + 'click .cancel': 'hide', + 'click .apply': '_onClickApply', + 'change #canopy_slider': '_onChangeSlider', + 'input #canopy_slider': '_onDragSlider', + 'click .slider_option': '_onClickOption' }, initialize: function() { @@ -92,8 +97,9 @@ gfw.ui.view.umdoptionsDialog = gfw.ui.view.Widget.extend({ this.model.on('change:hidden', this.toggle); this.model.on('change:mode', this._toggleMode); + this.canopy = 75; - var template = $('#umdoptions_dialog-template').html(); + var template = $('#umdoptions_dialog-template').html(); this.template = new cdb.core.Template({ template: template, @@ -101,22 +107,98 @@ gfw.ui.view.umdoptionsDialog = gfw.ui.view.Widget.extend({ }); }, - show: function() { - this.$el.fadeIn(250); + _onDragSlider: function() { + this._paintRange($('#canopy_slider').val()) }, - hide: function(e) { - e && e.preventDefault(); + _onChangeSlider: function() { + this._paintRange($('#canopy_slider').val()) + }, - var that = this; + _onClickOption: function(e) { + switch($(e.target).data('option')) { + case 0: + case 100: + return true; + break; + case 10: + this._paintRange(15) + break; + case 15: + this._paintRange(25) + break; + case 20: + this._paintRange(35) + break; + case 25: + this._paintRange(45) + break; + case 30: + this._paintRange(55) + break; + case 50: + this._paintRange(65) + break; + case 75: + this._paintRange(75) + break; + } + }, - this.$el.fadeOut(250); + _paintRange: function(slider_val) { + switch(true) { + case slider_val >= 10 && slider_val < 18: + slider_val = 10; + this.canopy = 10; + $('.visible_range').css('width', 60 + 281); + break; + case slider_val >= 18 && slider_val < 28: + slider_val = 22; //15 + this.canopy = 15; + $('.visible_range').css('width', 60 + 232); + break; + case slider_val >= 28 && slider_val < 38: + slider_val = 32; //20 + this.canopy = 20; + $('.visible_range').css('width', 60 + 190); + break; + case slider_val >= 38 && slider_val < 48: + slider_val = 43; //25 + this.canopy = 25; + $('.visible_range').css('width', 60 + 142); + break; + case slider_val >= 48 && slider_val < 58: + slider_val = 54; //30 + this.canopy = 30; + $('.visible_range').css('width', 60 + 95); + break; + case slider_val >= 58 && slider_val < 69: + slider_val = 65; //50 + this.canopy = 50; + $('.visible_range').css('width', 60 + 52); + break; + case slider_val >= 69 && slider_val < 100: + default: + slider_val = 75; + this.canopy = 75; + $('.visible_range').css('width', 60); + break; + } + document.getElementById('canopy_slider').value = slider_val; }, - _onEscKey: function(e) { + _onClickApply: function(e) { e && e.preventDefault(); + console.log(this.canopy) + }, + + show: function() { + this.$el.fadeIn(250); + }, - this.hide(); + hide: function(e) { + e && e.preventDefault(); + this.$el.fadeOut(250); }, render: function() { From 7eb604ecf4d2c1fb4b53ac428f62d28480152219 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A1n=20P=C3=A9rez?= Date: Tue, 27 May 2014 17:29:41 +0200 Subject: [PATCH 016/823] umd intensity: slider fixes --- .../javascripts/gfw/ui/umd_options.js.erb | 28 +++++++++++-------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/lib/assets/javascripts/gfw/ui/umd_options.js.erb b/lib/assets/javascripts/gfw/ui/umd_options.js.erb index 4cefff08dd..43837afb4e 100644 --- a/lib/assets/javascripts/gfw/ui/umd_options.js.erb +++ b/lib/assets/javascripts/gfw/ui/umd_options.js.erb @@ -153,29 +153,29 @@ gfw.ui.view.umdoptionsDialog = gfw.ui.view.Widget.extend({ $('.visible_range').css('width', 60 + 281); break; case slider_val >= 18 && slider_val < 28: - slider_val = 22; //15 + slider_val = 21; //15 this.canopy = 15; - $('.visible_range').css('width', 60 + 232); + $('.visible_range').css('width', 60 + 233); break; case slider_val >= 28 && slider_val < 38: - slider_val = 32; //20 + slider_val = 31; //20 this.canopy = 20; - $('.visible_range').css('width', 60 + 190); + $('.visible_range').css('width', 60 + 192); break; case slider_val >= 38 && slider_val < 48: - slider_val = 43; //25 + slider_val = 42; //25 this.canopy = 25; - $('.visible_range').css('width', 60 + 142); + $('.visible_range').css('width', 60 + 143); break; case slider_val >= 48 && slider_val < 58: - slider_val = 54; //30 + slider_val = 53; //30 this.canopy = 30; - $('.visible_range').css('width', 60 + 95); + $('.visible_range').css('width', 60 + 97); break; case slider_val >= 58 && slider_val < 69: - slider_val = 65; //50 + slider_val = 64; //50 this.canopy = 50; - $('.visible_range').css('width', 60 + 52); + $('.visible_range').css('width', 60 + 54); break; case slider_val >= 69 && slider_val < 100: default: @@ -189,7 +189,13 @@ gfw.ui.view.umdoptionsDialog = gfw.ui.view.Widget.extend({ _onClickApply: function(e) { e && e.preventDefault(); - console.log(this.canopy) + console.log(this.canopy); + GFW.app. + this.hide() + }, + + updateMap: function() { + publish('timeline:change_date_fires', [this.model.get('end_day')]); }, show: function() { From d46d6e7920b3462ecf9d8d0f2af8c663e8d6757a Mon Sep 17 00:00:00 2001 From: Pedro Garcia Date: Tue, 27 May 2014 18:46:58 +0200 Subject: [PATCH 017/823] forma graph --- .../images/backgrounds/country-indepth.png | Bin 0 -> 2526 bytes .../javascripts/countries/views/show.js | 149 +++++++++--------- app/assets/stylesheets/countries/show.scss | 70 +++++++- app/views/countries/show.html.erb | 5 +- 4 files changed, 150 insertions(+), 74 deletions(-) create mode 100644 app/assets/images/backgrounds/country-indepth.png diff --git a/app/assets/images/backgrounds/country-indepth.png b/app/assets/images/backgrounds/country-indepth.png new file mode 100644 index 0000000000000000000000000000000000000000..3da4ab5d2e4b4f3f5adaeda2b13efd6b461e3f36 GIT binary patch literal 2526 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 literal 0 HcmV?d00001 diff --git a/app/assets/javascripts/countries/views/show.js b/app/assets/javascripts/countries/views/show.js index a99fa95288..1c43d82108 100644 --- a/app/assets/javascripts/countries/views/show.js +++ b/app/assets/javascripts/countries/views/show.js @@ -337,8 +337,10 @@ gfw.ui.view.CountriesShow = cdb.core.View.extend({ var sql = "SELECT year, loss_gt_0 loss FROM umd WHERE iso='" + this.country.get('iso') + "'", that = this; - var $amount = this.$('.loss-gain-graph .graph-amount'), - $date = this.$('.loss-gain-graph .graph-date'); + var $graph = this.$('.loss-gain-graph'), + $comingSoon = $graph.find('.coming-soon'), + $amount = $graph.find('.graph-amount'), + $date = $graph.find('.graph-date'); var width = 200, height = 90, @@ -351,18 +353,15 @@ gfw.ui.view.CountriesShow = cdb.core.View.extend({ .attr('height', height); d3.json('https://wri-01.cartodb.com/api/v2/sql?q=' + sql, function(json) { - var data = json.rows; - var data_ = []; - - // if (json) { - // $graph.removeClass('ghost'); - - // var data = json.rows; - // } else { - // $coming_soon.show(); + if (json) { + $graph.removeClass('ghost'); + var data = json.rows; + } else { + $comingSoon.show(); + return; + } - // return; - // } + var data_ = []; _.each(data, function(val, key) { if (val.year >= 2001) { @@ -426,9 +425,6 @@ gfw.ui.view.CountriesShow = cdb.core.View.extend({ d3.json('https://wri-01.cartodb.com/api/v2/sql?q=' + encodeURIComponent(sql), function(json) { var gainAverage = json.rows[0].gain / 12; - console.log(gainAverage); - console.log(Math.abs(y_scale(gainAverage))); - var gainLine = graph.append('svg:svg') .attr('class', 'line') .attr('width', width) @@ -487,7 +483,9 @@ gfw.ui.view.CountriesShow = cdb.core.View.extend({ path.enter().append('text') .attr('transform', function(d) { var c = arc.centroid(d); return 'translate(' + (c[0]-12) + ',' + (c[1]+8) + ')'}) - .text(function(d) { return d.data + '%' }) + .text(function(d) { + if (d.data > 0) return d.data + '%' + }) .attr('fill', function(d, i) { return labelColors[i]; } ) .style('font-size', '13px'); }); @@ -496,21 +494,27 @@ gfw.ui.view.CountriesShow = cdb.core.View.extend({ _drawFormaAlerts: function() { var that = this; - var $graph = $('.forma-graph'); + var $graph = this.$('.forma-graph'), + $tooltip = this.$('.graph-tooltip') + $amount = $tooltip.find('.graph-amount'), + $date = $tooltip.find('.graph-date'), + $comingSoon = this.$('.forma-graph'); + // Dimensions variables var width = 500, height = 156, h = 156, // maxHeight radius = width / 2, gridLinesCount = 7; - // Add dashed grid + // Init graph var graph = d3.select('.forma-graph') .append('svg:svg') .attr('class', 'line') .attr('width', width) .attr('height', height); + // Add dashed lines grid var gridLineY = height; for (var i = 0; i < gridLinesCount; i++) { graph.append('svg:line') @@ -536,70 +540,73 @@ gfw.ui.view.CountriesShow = cdb.core.View.extend({ var data = json.rows.slice(1, json.rows.length); } else { console.log('no data'); - //$coming_soon.show(); + // $comingSoon.show(); return; }; - var x_scale = d3.scale.linear() - .domain([0, data.length - 1]) - .range([0, width - 80]); - - var max = d3.max(data, function(d) { return parseFloat(d.alerts); }); + var x_scale = d3.scale.linear() + .domain([0, data.length - 1]) + .range([0, width - 40]); - if (max === d3.min(data, function(d) { return parseFloat(d.alerts); })) { - h = h/2; - } - - var y_scale = d3.scale.linear() - .domain([0, max]) - .range([0, h]); - - var line = d3.svg.line() - .x(function(d, i) { return x_scale(i); }) - .y(function(d, i) { return h - y_scale(d.alerts); }) - .interpolate('basis'); - - var marginLeft = 40, - marginTop = radius - h/2; - - //$amount.html(''+formatNumber(data[data.length - 1].alerts)+''); - - var date = new Date(data[data.length - 1].date), - form_date = 'Alerts in ' + config.MONTHNAMES[date.getUTCMonth()] + ' ' + date.getUTCFullYear(); - - //$date.html(form_date); + var max = d3.max(data, function(d) { return parseFloat(d.alerts); }); + if (max === d3.min(data, function(d) { return parseFloat(d.alerts); })) { + h = h/2; + } - var cx = width - 80 + marginLeft; - var cy = h - y_scale(data[data.length - 1].alerts) + marginTop; + var y_scale = d3.scale.linear() + .domain([0, max]) + .range([0, h]); + + var line = d3.svg.line() + .x(function(d, i) { return x_scale(i); }) + .y(function(d, i) { console.log(y_scale(d.alerts));return h - y_scale(d.alerts); }) + .interpolate("basis"); + + // var marginLeft = 40, + // marginTop = radius - h/2; + var marginTop = 0, + marginLeft = 20; + + // Default amount: + // $amount.html('' + formatNumber(data[data.length - 1].alerts) + ''); + + // Default date: + // var date = new Date(data[data.length - 1].date), + // form_date = 'Alerts in ' + config.MONTHNAMES[date.getUTCMonth()] + ' ' + date.getUTCFullYear(); + // $date.html(form_date); + + var cx = width - 40 + marginLeft; + var cy = h - y_scale(data[data.length - 1].alerts) + marginTop; + + var marker = graph.append('svg:circle') + .attr('class', 'forma_marker') + .attr('cx', cx) + .attr('cy', cy) + .attr('r', 5); - graph.append('svg:path') - .attr('transform', 'translate(' + marginLeft + ',' + marginTop + ')') - .attr('d', line(data)) - .on('mousemove', function(d) { - var index = Math.round(x_scale.invert(d3.mouse(this)[0])); + graph.append('svg:path') + .attr('transform', 'translate(' + marginLeft + ',' + marginTop + ')') + .attr('d', line(data)) + .on('mousemove', function(d) { + // var index = Math.round(x_scale.invert(d3.mouse(this)[0])); - if (data[index]) { // if there's data - //$amount.html(''+formatNumber(data[index].alerts)+''); + // if (data[index]) { // if there's data + // // $amount.html(''+formatNumber(data[index].alerts)+''); - var date = new Date(data[index].date), - form_date = 'Alerts in ' + config.MONTHNAMES[date.getUTCMonth()] + ' ' + date.getUTCFullYear(); + // // var date = new Date(data[index].date), + // // form_date = 'Alerts in ' + config.MONTHNAMES[date.getUTCMonth()] + ' ' + date.getUTCFullYear(); - //$date.html(form_date); + // // $date.html(form_date); - var cx = d3.mouse(this)[0] + marginLeft; - var cy = h - y_scale(data[index].alerts) + marginTop; + // var cx = d3.mouse(this)[0] + marginLeft; + // var cy = h - y_scale(data[index].alerts); - graph.select('.forma_marker') - .attr('cx', cx) - .attr('cy', cy); - } - }); + // graph.select('.forma_marker') + // .attr('cx', cx) + // .attr('cy', cy); + //} + }); - graph.append('svg:circle') - .attr('class', 'forma_marker') - .attr('cx', cx) - .attr('cy', cy) - .attr('r', 5); }); }, diff --git a/app/assets/stylesheets/countries/show.scss b/app/assets/stylesheets/countries/show.scss index d0e9214ce8..1f8c7d70cb 100644 --- a/app/assets/stylesheets/countries/show.scss +++ b/app/assets/stylesheets/countries/show.scss @@ -121,6 +121,32 @@ .loss-gain-graph { width: 35%; + position: relative; + + .coming-soon { + display: none; + position: absolute; + z-index: 10; + top: 70px; + left: 10px; + padding: 30px 0; + margin-right: 10px; + font-size: 17px; + text-transform: uppercase; + text-align: center; + color: #999; + background: $default-color; + + span { + width: 90%; + display: inline-block; + } + } + + &.ghost { + @include opacity(.5); + cursor: default; + } h4 { margin-bottom: 20px; @@ -205,8 +231,9 @@ padding: 35px 20px; font-family: $font-medium; @include box-sizing(border-box); - - //@include country-icons-sprite(country-indepth); + background-image: image-url('backgrounds/country-indepth.png'); + background-repeat: no-repeat; + background-position: 96% 50%; .country-indepth__title { font-size: 29px; @@ -371,6 +398,45 @@ display: none; } + .forma-graph path { + stroke: $primary-color; + stroke-width: 4px; + fill: none; + } + + .coming-soon { + display: none; + } + + .graph-tooltip { + position: absolute; + width: 150px; + height: 84px; + background: $primary-color; + @include box-sizing(border-box); + padding: 12px 20px; + border-radius: 5px; + text-align: center; + z-index: 9999; + + .graph-amount { + font-size: 29px; + color: white; + margin-bottom: 4px; + display: block; + } + + .graph-date { + font-size: 12px; + color: #60644D; + .date { + margin-top: 2px; + display: block; + font-size: 15px; + } + } + } + .forma-alerts-legend { margin: 5px 0 25px 0; diff --git a/app/views/countries/show.html.erb b/app/views/countries/show.html.erb index 4a1d43cf76..7e7331a525 100644 --- a/app/views/countries/show.html.erb +++ b/app/views/countries/show.html.erb @@ -44,11 +44,12 @@ mha -
    +

    LOSS AND GAIN (2001 - 2012)

    +
    Not yet available for this country
    @@ -87,6 +88,7 @@
    <% end %> + @@ -138,6 +140,7 @@
    +
    Not yet available for this country
    From b26716955ed7d039b05864ab27332c8bec76b795 Mon Sep 17 00:00:00 2001 From: rschumann Date: Wed, 28 May 2014 09:58:37 +0200 Subject: [PATCH 018/823] update for filter tests on map page --- .gitignore | 1 + public/media/upload/big_coffee-2.jpg | Bin 54987 -> 0 bytes public/media/upload/big_default_taxon.png | Bin 60249 -> 0 bytes public/media/upload/coffee-2.jpg | Bin 268617 -> 0 bytes public/media/upload/default_taxon.png | Bin 3808 -> 0 bytes public/media/upload/thumb_coffee-2.jpg | Bin 18845 -> 0 bytes public/media/upload/thumb_default_taxon.png | Bin 6207 -> 0 bytes spec/features/map_page_spec.rb | 123 ++++++++++++++++++++ 8 files changed, 124 insertions(+) delete mode 100644 public/media/upload/big_coffee-2.jpg delete mode 100644 public/media/upload/big_default_taxon.png delete mode 100644 public/media/upload/coffee-2.jpg delete mode 100644 public/media/upload/default_taxon.png delete mode 100644 public/media/upload/thumb_coffee-2.jpg delete mode 100644 public/media/upload/thumb_default_taxon.png diff --git a/.gitignore b/.gitignore index 8dae92e7c8..966bf322ab 100644 --- a/.gitignore +++ b/.gitignore @@ -16,6 +16,7 @@ /tmp /media/upload /public/uploads +/public/media .DS_Store diff --git a/public/media/upload/big_coffee-2.jpg b/public/media/upload/big_coffee-2.jpg deleted file mode 100644 index 25236025f4db34a268db6e3628d0d0b36a3d3bf8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 54987 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%0D(srGOop4k7lhANoDP&n=@* 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 E+0QTCiU0rr diff --git a/public/media/upload/big_default_taxon.png b/public/media/upload/big_default_taxon.png deleted file mode 100644 index 33c6a5fbe601ab1d93eea42c5268daf6d9eba41e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 60249 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�Mg{Mh<@O?@d&d?R+Gc%o&o?0pr*Jnb~##VOI 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�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 diff --git a/public/media/upload/default_taxon.png b/public/media/upload/default_taxon.png deleted file mode 100644 index 6026a1bdd477df9e8489434528de1eeb5b6a348c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3808 zcmdT{X*d)N7xh+Ng~(1q$-X2?mKml(89QTNvy44smu+OvHpC!n846<=BTU9JWzD{g z5tF8Kuz?u(CpUqGU zr(w2Jk-fC`!TukEzWhFvTh>UKN^@D8glmAO)a&=YH$9JT<&=EFd2;?xZm|Xyx}Nf* z$$diEK%m8QffsR2*RMtvC6S!slFXUQ$CB6ym?axVxw*Db&k&IuYUl#D@^|aXT(3i8SyXh zuhQwSf0b95{wG%7#Kzp%r^!EhJ;v0lCQ4z#8#OQ8iWK4N+kPKKv4IlIx20H5O4Xv{ zc_m)#fF^zK)NpfK!o_Ucmn}j>v@Gt>w|JzOl11}aGMqhBrp0@<9{5=E?!K*}#SKYK zbI&NI3U(cybiErsh<7_N%Fuc+Y?x&Uujxtz=d?b*B+Qd?%_-*N1m8m`6FFd-W1OX3!>Obw!5kg zKs)0`MqqFI7E`3VI`$Wu1iHxxoLQTY({W{V(yF#KW}f$tZ{!Dg)tp#!YH8*4$vS@b z2-*%j*(F>GE~;{MK@R%5XN!0yI^Iu|w-U{YTYZOQR7Z!eQ+;A|i^fV@3 z4JBdLA!Zz%_Z3X^GM;6lfiFg)3X)8&C@g)067WUO4$~V>JvO9hsWFgm-VvL%xG1^& zo7cwZO`MNYQN3mD9->M&ECDf)(u+_#c%toCy|bxrVKePen&-TiMfy@fU(9za1&3xv zzVU!jBay38$yh zY@E_Tzk{qMB_A3Ku%{xg(6U}(y27bL{K(b@?`ThhSJ(V`qH1{%DUhthHLn z{wLO(d%Z62!iN9cH%$u;{jv6U5uKoFkhyexVnJDf`avtl3+i`xT@blySJOKZtp zuISCCQEfbZ3NcT(EckJ9I%vTHb(qo1(iIUyo1#AP_)tv5CM*LhTsL~q~^Ekxb}jbogBu%zq^ad7u-e!$L5%wq(`kb|7t>^k(+rhFOtob z-gYZ^2FER8ak*beHQ#doBr8E$9wGiV@53dxo3VF1M=)DIw)A;1=w`6!Vs(gb{fx@X zOhp#*g+|1=;nWYyxIxTB^AMUNtL~xMGv7M6S zOwjApGjQ-FflBAbj>I(b1;@+ApY~%L@((n?DOmw1v@Tjw3SZ&Aj^hU4&d%=zePu)| zY}py7A598Srb}ohB2LAfGa*DOuK>5|g2 zWP)5dL*NZHYq}a8RiES34`j4gGR=K9$TIRzN%lk^X z7w#pk_ucq|1E|GWvRMb-?ll0r>I|eMh1J`+;(vDYs<06Uz$FxXqbE5_v{ET|t2(o+ zyR}r!d^3{?qe!}S^ti>-M=yh`Ja7c?b))Q-=L>%YL#Sb@8`n+A-f$5`|8Sks5BJd` z_1HX~pQiRnJ3!UTbAD2RYu|p{>K2m)IogGffM&83N*f1lb#FgTNYvM_&BkS~T96aU zmuJ4WR)}(TmOb9Kfd}P!zOo2i7T}U>Ix4evLvppfw@**r(Tt)D)~PE`kvR|D*DIll zO0~(>h1UF&yrMbD`s!|WQ6S(~y{fPXYPY7s3is4W_v_G43)vwA56P^hll}yGx5gTtQ zGfUN0m6QCH$RY2P$WAF^#j{_x3;>7mF=j1p4j_W-TWr25<98te$Ei7d!kryqp})RNpA_j&z!Fb14TuoR zN2J}fhwY!FGV!zyY!^0{+NSt)KaNI}$~`6V0^YI?1`nU34=y^r`GV`x)fAqV_0%xZ zGPpEqeoWWEqZ5BxTqf?6wY0o**pHFO-Yc6J|D?Gg3bO-tCF-A5Z;J09nop z;o;Nx!x0yo)xm;H)cz3K!dfXIeTzO22L{=8Ys$?z;c+^>y<){#&UrI6>A))D*j@R( zOi!_FmtqA#{9dfXX$5vS^g{1Fyb~B(OsrHQv?nLfw0)|UDkAA&Zwg{masY?=Wl zj4GP?Y2t7kpFj43R(fA0JbVoo!S^Y(*PvZcHElJUz?8qfH58!&|FCpGD=<_Wz)Y}4 ztTtVd)&W;pJ%#VQl?Zm?@_I_Hm9;PPL;^SMoZS%@Hmf7U*K*FBe4Y;uG?OXsG!r0gv)wud=#sL`T&r}-D_wnf40&(&`{6gMLD z{Ok>O^Gd~A9KAx~XYxquu+O-UfT2;Zpea5DdSjA=)}{3}CCcKKf@s)eL+zit%rP4+ z47Kk!MZ1!Pi96MDrzMRo)+a-AcV1Hxl2zdNw$W}&-Y={N-M>9IDhaHpg6q_ZD%6ENOxnBmTG_K&)ICs>^YN>q#gZ{D=~v>Rquwi9<%(Gu}8j^p3k} z;e38e_!O)+)U>*yl+rWQ5upS>uy@|nz^w+yZelak9ZJsgptEVI7d5TB$= zhQ$!aPyB)WcUcO_P@I1P+SF?yAI2r0dSz{|=D`>9g^hA@$qL_L*in7mZGf@Ed=q$= zZ7xs?t83mc2C4a(F1c!fX_eN5NeguyI?Q{|)e;A+;SbjZe(>l7cC^D7WTkzt9;bYm zO`)y3IZo2p$D~XC>Hz%L$kg?JlBxEi=0WtlbY<8#%>se6q_fCM2i7vstbO<-=0DW$ BJw^Zk diff --git a/public/media/upload/thumb_coffee-2.jpg b/public/media/upload/thumb_coffee-2.jpg deleted file mode 100644 index 87ae1e8b5133c9a480142ebca077ffaab8047b02..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 18845 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 diff --git a/public/media/upload/thumb_default_taxon.png b/public/media/upload/thumb_default_taxon.png deleted file mode 100644 index ec17087c45089839b744319a3bd76163098e98ff..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6207 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 diff --git a/spec/features/map_page_spec.rb b/spec/features/map_page_spec.rb index 86eb29af7c..265a66fcb6 100644 --- a/spec/features/map_page_spec.rb +++ b/spec/features/map_page_spec.rb @@ -13,6 +13,129 @@ expect(page).to have_css("div#filters li.filter.forest_change") end + scenario 'Select date range', js: true do + visit '/map' + expect(page).to have_css(".filters", visible: true) + find('.timeline.timeline_loss .trail .handle.left').click + find('.timeline.timeline_loss .years').click_link('2003') + scripttrailr = "$('.timeline.timeline_loss .trail .handle.right').show().click();" + page.driver.browser.execute_script(scripttrailr) + find('.timeline.timeline_loss .years').click_link('2009') + page.current_url.should include('/loss/596?begin=2003&end=2009') + page.driver.browser.action.move_to(page.find('ul.filters-list li.forest_change').native).perform + page.driver.browser.action.move_to(page.find('li#filter_596').native).perform + click_link 'Gain' + page.current_url.should include('/loss?begin=2003&end=2009') + page.driver.browser.action.move_to(page.find('ul.filters-list li.forest_change').native).perform + page.driver.browser.action.move_to(page.find('li#filter_596').native).perform + click_link 'Gain' + page.current_url.should include('/loss/596?begin=2003&end=2009') + page.driver.browser.action.move_to(page.find('ul.filters-list li.forest_change').native).perform + page.driver.browser.action.move_to(page.find('li#filter_595').native).perform + click_link 'Loss' + page.current_url.should include('/none/596') + end + + scenario 'Clicking forest change filter links', js: true do + visit '/map' + expect(page).to have_css(".filters", visible: true) + page.driver.browser.action.move_to(page.find('ul.filters-list li.forest_change').native).perform + page.driver.browser.action.move_to(page.find('li#filter_569').native).perform + click_link 'FORMA alerts' + page.current_url.should include('/forma?begin=2006-01-01&end=2014-03-01') + page.driver.browser.action.move_to(page.find('ul.filters-list li.forest_change').native).perform + page.driver.browser.action.move_to(page.find('li#filter_584').native).perform + click_link 'Imazon SAD alerts' + page.current_url.should include('/imazon?begin=2006-01-01&end=2014-03-01') + page.driver.browser.action.move_to(page.find('ul.filters-list li.forest_change').native).perform + page.driver.browser.action.move_to(page.find('li#filter_588').native).perform + click_link 'QUICC alerts' + page.current_url.should include('/modis?date=2014-3') + page.driver.browser.action.move_to(page.find('ul.filters-list li.forest_change').native).perform + page.driver.browser.action.move_to(page.find('li#filter_593').native).perform + click_link 'NASA active fires' + page.current_url.should include('/fires?date=2014-3') + page.driver.browser.action.move_to(page.find('ul.filters-list li.forest_change').native).perform + page.driver.browser.action.move_to(page.find('li#filter_593').native).perform + click_link 'NASA active fires' + page.current_url.should include('/fires?date=2014-3') + end + scenario 'Clicking forest cover filter links', js: true do + visit '/map' + expect(page).to have_css(".filters", visible: true) + page.driver.browser.action.move_to(page.find('ul.filters-list li.forest_cover').native).perform + page.driver.browser.action.move_to(page.find('li#filter_591').native).perform + click_link 'Tree cover extent' + page.current_url.should include('/loss/596,591?begin=2000&end=2013') + page.driver.browser.action.move_to(page.find('ul.filters-list li.forest_cover').native).perform + page.driver.browser.action.move_to(page.find('li#filter_558').native).perform + click_link 'Intact forest landscapes' + page.current_url.should include('/loss/596,591,558?begin=2000&end=2013') + page.driver.browser.action.move_to(page.find('ul.filters-list li.forest_cover').native).perform + page.driver.browser.action.move_to(page.find('li#filter_590').native).perform + click_link 'Tropical forest carbon stocks' + page.current_url.should include('/loss/596,591,558,590?begin=2000&end=2013') + page.driver.browser.action.move_to(page.find('ul.filters-list li.forest_cover').native).perform + page.driver.browser.action.move_to(page.find('li#filter_591').native).perform + click_link 'Tree cover extent' + page.current_url.should include('/loss/596,558,590?begin=2000&end=2013') + end + + scenario 'Clicking forest use filter links', js: true do + visit '/map' + expect(page).to have_css(".filters", visible: true) + page.driver.browser.action.move_to(page.find('ul.filters-list li.forest_use').native).perform + page.driver.browser.action.move_to(page.find('li#filter_581').native).perform + click_link 'Logging' + page.current_url.should include('/loss/596,581?begin=2000&end=2013') + page.driver.browser.action.move_to(page.find('ul.filters-list li.forest_use').native).perform + page.driver.browser.action.move_to(page.find('li#filter_573').native).perform + find('#filter_573').click_link('Mining') + page.current_url.should include('/loss/596,581,573?begin=2000&end=2013') + page.driver.browser.action.move_to(page.find('ul.filters-list li.forest_use').native).perform + page.driver.browser.action.move_to(page.find('li#filter_556').native).perform + click_link 'Oil palm' + page.current_url.should include('/loss/596,581,573,556?begin=2000&end=2013') + page.driver.browser.action.move_to(page.find('ul.filters-list li.forest_use').native).perform + page.driver.browser.action.move_to(page.find('li#filter_582').native).perform + click_link 'Wood fiber plantations' + page.current_url.should include('/loss/596,581,573,556,582?begin=2000&end=2013') + end + + scenario 'Clicking conservation filter links', js: true do + visit '/map' + expect(page).to have_css(".filters", visible: true) + page.driver.browser.action.move_to(page.find('ul.filters-list li.conservation').native).perform + page.driver.browser.action.move_to(page.find('li#filter_574').native).perform + find('#filter_574').click_link('Protected areas') + page.current_url.should include('/loss/596,574?begin=2000&end=2013') + page.driver.browser.action.move_to(page.find('ul.filters-list li.conservation').native).perform + page.driver.browser.action.move_to(page.find('li#filter_592').native).perform + find('#filter_592').click_link('Biodiversity hotspots') + page.current_url.should include('/loss/596,574,592?begin=2000&end=2013') + end + + scenario 'Clicking people filter links', js: true do + visit '/map' + expect(page).to have_css(".filters", visible: true) + page.driver.browser.action.move_to(page.find('ul.filters-list li.people').native).perform + page.driver.browser.action.move_to(page.find('li#filter_599').native).perform + click_link 'Resource Rights' + page.current_url.should include('/loss/596,599?begin=2000&end=2013') + end + + scenario 'Clicking stories filter links', js: true do + visit '/map' + expect(page).to have_css(".filters", visible: true) + page.driver.browser.action.move_to(page.find('ul.filters-list li.stories').native).perform + page.driver.browser.action.move_to(page.find('li#filter_580').native).perform + click_link 'User stories' + page.current_url.should include('/loss/596,580?begin=2000&end=2013') + page.driver.browser.action.move_to(page.find('ul.filters-list li.stories').native).perform + page.driver.browser.action.move_to(page.find('li#filter_586').native).perform + click_link 'Mongabay stories' + page.current_url.should include('/loss/596,580,586?begin=2000&end=2013') + end end \ No newline at end of file From 0cc75d8271e3c9940e2a14abbc5a3b3c235034d5 Mon Sep 17 00:00:00 2001 From: Pedro Garcia Date: Wed, 28 May 2014 13:25:37 +0200 Subject: [PATCH 019/823] forma alerts --- .../javascripts/countries/views/show.js | 108 ++++++++++++------ app/assets/stylesheets/countries/show.scss | 55 +++++++-- app/views/countries/show.html.erb | 9 +- 3 files changed, 128 insertions(+), 44 deletions(-) diff --git a/app/assets/javascripts/countries/views/show.js b/app/assets/javascripts/countries/views/show.js index 1c43d82108..32e07b0b49 100644 --- a/app/assets/javascripts/countries/views/show.js +++ b/app/assets/javascripts/countries/views/show.js @@ -498,12 +498,15 @@ gfw.ui.view.CountriesShow = cdb.core.View.extend({ $tooltip = this.$('.graph-tooltip') $amount = $tooltip.find('.graph-amount'), $date = $tooltip.find('.graph-date'), - $comingSoon = this.$('.forma-graph'); + $comingSoonContent = this.$('#comingSoonContent'), + $formaAlertsContent = this.$('#formaAlertsContent'), + $formaAlertsTitle = this.$('#formaAlertsTitle'); + // Dimensions variables var width = 500, height = 156, - h = 156, // maxHeight + h = 136, // maxHeight radius = width / 2, gridLinesCount = 7; @@ -538,9 +541,14 @@ gfw.ui.view.CountriesShow = cdb.core.View.extend({ d3.json('https://wri-01.cartodb.com/api/v2/sql?q='+sql, function(json) { if (json && json.rows.length > 0) { var data = json.rows.slice(1, json.rows.length); + + var lastMonth = data[data.length - 1]; + $formaAlertsTitle.find('.amount').text(formatNumber(lastMonth.alerts)); + $formaAlertsTitle.find('.month').text(config.MONTHNAMES[new Date(lastMonth.date).getUTCMonth()]) + + $formaAlertsContent.show(); } else { - console.log('no data'); - // $comingSoon.show(); + $comingSoonContent.show(); return; }; @@ -559,52 +567,84 @@ gfw.ui.view.CountriesShow = cdb.core.View.extend({ var line = d3.svg.line() .x(function(d, i) { return x_scale(i); }) - .y(function(d, i) { console.log(y_scale(d.alerts));return h - y_scale(d.alerts); }) - .interpolate("basis"); + .y(function(d, i) { return h - y_scale(d.alerts); }) + .interpolate("monotone"); - // var marginLeft = 40, - // marginTop = radius - h/2; - var marginTop = 0, + var marginTop = 20, marginLeft = 20; - // Default amount: - // $amount.html('' + formatNumber(data[data.length - 1].alerts) + ''); - - // Default date: - // var date = new Date(data[data.length - 1].date), - // form_date = 'Alerts in ' + config.MONTHNAMES[date.getUTCMonth()] + ' ' + date.getUTCFullYear(); - // $date.html(form_date); - var cx = width - 40 + marginLeft; var cy = h - y_scale(data[data.length - 1].alerts) + marginTop; - var marker = graph.append('svg:circle') - .attr('class', 'forma_marker') + var tooltip = d3.select('.forma-graph') + .append('div') + .attr('class', 'graph-tooltip') + .style('visibility', 'hidden') + + var amount = tooltip + .append('div') + .attr('class', 'graph-amount') + .text('21,123') + + tooltip + .append('div') + .attr('class', 'graph-date') + .text('Alerts in') + + var tooltipDate = tooltip.select('.graph-date') + .append('div') + .attr('class', 'date') + .text('November 2012'); + + graph.append('svg:path') + .attr('transform', 'translate(' + marginLeft + ',' + marginTop + ')') + .attr('d', line(data)); + + var positioner = graph.append('svg:line') + .attr('x1', 0) + .attr('y1', 0) + .attr('x2', 0) + .attr('y2', height) + .style('visibility', 'hidden') + .style('stroke', '#aaa'); + + graph.append('svg:circle') + .attr('class', 'forma-marker') .attr('cx', cx) .attr('cy', cy) .attr('r', 5); - graph.append('svg:path') - .attr('transform', 'translate(' + marginLeft + ',' + marginTop + ')') - .attr('d', line(data)) + graph + .on("mouseout", function() { + positioner.style("visibility", "hidden"); + tooltip.style("visibility", "hidden"); + }) + .on("mouseover", function() { + positioner.style("visibility", "visible"); + tooltip.style("visibility", "visible"); + }) .on('mousemove', function(d) { - // var index = Math.round(x_scale.invert(d3.mouse(this)[0])); + positioner.attr('x1', d3.mouse(this)[0] + marginLeft); + positioner.attr('x2', d3.mouse(this)[0] + marginLeft); - // if (data[index]) { // if there's data - // // $amount.html(''+formatNumber(data[index].alerts)+''); + var index = Math.round(x_scale.invert(d3.mouse(this)[0])), + cx = d3.mouse(this)[0] + marginLeft, + cy = h - y_scale(data[index].alerts) + marginTop; - // // var date = new Date(data[index].date), - // // form_date = 'Alerts in ' + config.MONTHNAMES[date.getUTCMonth()] + ' ' + date.getUTCFullYear(); + var marker = graph.select('.forma-marker') + .attr('cx', cx) + .attr('cy', cy); - // // $date.html(form_date); + if (data[index]) { // if there's data + amount.text(formatNumber(data[index].alerts)); - // var cx = d3.mouse(this)[0] + marginLeft; - // var cy = h - y_scale(data[index].alerts); + var date = new Date(data[index].date), + form_date = config.MONTHNAMES[date.getUTCMonth()] + ' ' + date.getUTCFullYear(); + + tooltipDate.text(form_date); + } - // graph.select('.forma_marker') - // .attr('cx', cx) - // .attr('cy', cy); - //} + tooltip.style("top", "-20px").style("left", (cx - 162) + "px"); }); }); diff --git a/app/assets/stylesheets/countries/show.scss b/app/assets/stylesheets/countries/show.scss index 1f8c7d70cb..4b444c4ef4 100644 --- a/app/assets/stylesheets/countries/show.scss +++ b/app/assets/stylesheets/countries/show.scss @@ -394,6 +394,21 @@ } .country-alerts { + #formaAlertsContent, + #comingSoonContent { + display: none; + } + + .coming-soon { + width: 100%; + font-size: 16px; + text-transform: uppercase; + text-align: center; + padding: 40px 0; + background: #f2f2f2; + color: #666; + } + .forma_dropdown-menu { display: none; } @@ -404,23 +419,30 @@ fill: none; } - .coming-soon { - display: none; + .forma-graph { + position: relative; + } + + .forma-marker { + stroke: white; + stroke-width: 2px; + fill: $primary-color; } .graph-tooltip { position: absolute; width: 150px; - height: 84px; + height: 78px; background: $primary-color; @include box-sizing(border-box); - padding: 12px 20px; + padding: 12px 10px; border-radius: 5px; text-align: center; - z-index: 9999; + z-index: 10; + pointer-events: none; .graph-amount { - font-size: 29px; + font-size: 26px; color: white; margin-bottom: 4px; display: block; @@ -432,9 +454,28 @@ .date { margin-top: 2px; display: block; - font-size: 15px; + font-size: 14px; } } + + @include box-shadow(#ddd 2px 2px 10px); + + &:before { + content:''; + display:block; + width:0; + height:0; + position:absolute; + + border-top: 8px solid transparent; + border-bottom: 8px solid transparent; + border-left: 8px solid $primary-color; + + right: -8px; + margin: auto; + top: 0; + bottom: 0; + } } .forma-alerts-legend { diff --git a/app/views/countries/show.html.erb b/app/views/countries/show.html.erb index 7e7331a525..b124a0bdf3 100644 --- a/app/views/countries/show.html.erb +++ b/app/views/countries/show.html.erb @@ -117,9 +117,13 @@ FORMA alerts -

    +
    +
    Not yet available for this country
    +
    + +
    -

    There were 7,363 FORMA alerts in october

    +

    There were FORMA alerts in

    Forest Clearing Alerts @@ -140,7 +144,6 @@
    -
    Not yet available for this country
    From c097ce6c8efce92a971d45aa6af905128ff65269 Mon Sep 17 00:00:00 2001 From: Pedro Garcia Date: Wed, 28 May 2014 13:34:09 +0200 Subject: [PATCH 020/823] refactoring forma alerts --- .../javascripts/countries/views/show.js | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/app/assets/javascripts/countries/views/show.js b/app/assets/javascripts/countries/views/show.js index 32e07b0b49..8a4172a29f 100644 --- a/app/assets/javascripts/countries/views/show.js +++ b/app/assets/javascripts/countries/views/show.js @@ -608,7 +608,7 @@ gfw.ui.view.CountriesShow = cdb.core.View.extend({ .style('visibility', 'hidden') .style('stroke', '#aaa'); - graph.append('svg:circle') + var marker = graph.append('svg:circle') .attr('class', 'forma-marker') .attr('cx', cx) .attr('cy', cy) @@ -624,27 +624,27 @@ gfw.ui.view.CountriesShow = cdb.core.View.extend({ tooltip.style("visibility", "visible"); }) .on('mousemove', function(d) { - positioner.attr('x1', d3.mouse(this)[0] + marginLeft); - positioner.attr('x2', d3.mouse(this)[0] + marginLeft); + var index = Math.round(x_scale.invert(d3.mouse(this)[0])); - var index = Math.round(x_scale.invert(d3.mouse(this)[0])), - cx = d3.mouse(this)[0] + marginLeft, - cy = h - y_scale(data[index].alerts) + marginTop; + if (data[index]) { + var cx = d3.mouse(this)[0] + marginLeft, + cy = h - y_scale(data[index].alerts) + marginTop, + date = new Date(data[index].date), + form_date = config.MONTHNAMES[date.getUTCMonth()] + ' ' + date.getUTCFullYear(); - var marker = graph.select('.forma-marker') - .attr('cx', cx) - .attr('cy', cy); + marker + .attr('cx', cx) + .attr('cy', cy); - if (data[index]) { // if there's data - amount.text(formatNumber(data[index].alerts)); - - var date = new Date(data[index].date), - form_date = config.MONTHNAMES[date.getUTCMonth()] + ' ' + date.getUTCFullYear(); + positioner + .attr('x1', d3.mouse(this)[0] + marginLeft) + .attr('x2', d3.mouse(this)[0] + marginLeft); + amount.text(formatNumber(data[index].alerts)); tooltipDate.text(form_date); + tooltip.style("top", "-20px").style("left", (cx - 162) + "px"); } - tooltip.style("top", "-20px").style("left", (cx - 162) + "px"); }); }); From 30d6245c758c2f232df034ceb51c2106edd32a88 Mon Sep 17 00:00:00 2001 From: Pedro Garcia Date: Wed, 28 May 2014 18:00:28 +0200 Subject: [PATCH 021/823] release candidate 0.1 --- .../javascripts/countries/views/show.js | 74 ++++++++++++++----- app/assets/stylesheets/application.scss | 14 ++++ app/assets/stylesheets/countries/show.scss | 53 +++++++++++-- app/views/countries/show.html.erb | 4 +- 4 files changed, 119 insertions(+), 26 deletions(-) diff --git a/app/assets/javascripts/countries/views/show.js b/app/assets/javascripts/countries/views/show.js index 8a4172a29f..eac887eb20 100644 --- a/app/assets/javascripts/countries/views/show.js +++ b/app/assets/javascripts/countries/views/show.js @@ -373,7 +373,7 @@ gfw.ui.view.CountriesShow = cdb.core.View.extend({ }); $amount.html('' + formatNumber(parseInt(data_[data_.length - 1].value, 10)) + ''); - $date.html('Hectares in ' + data_[data_.length - 1].year); + $date.html('Hectares lost in ' + data_[data_.length - 1].year); var marginLeft = 5, marginTop = 0; @@ -406,8 +406,8 @@ gfw.ui.view.CountriesShow = cdb.core.View.extend({ d3.selectAll('.bar').style('fill', '#524F5C'); d3.select(this).style('fill', '#9FBA2B'); - $amount.html(''+formatNumber(parseInt(d.value, 10))+''); - $date.html('Hectares in ' + d.year); + $amount.html('' + formatNumber(parseInt(d.value, 10)) + ''); + $date.html('Hectares lost in ' + d.year); }); // Draw gain line @@ -425,17 +425,44 @@ gfw.ui.view.CountriesShow = cdb.core.View.extend({ d3.json('https://wri-01.cartodb.com/api/v2/sql?q=' + encodeURIComponent(sql), function(json) { var gainAverage = json.rows[0].gain / 12; - var gainLine = graph.append('svg:svg') - .attr('class', 'line') + var tooltip = d3.select('.loss-gain-graph') + .append('div') + .attr('class', 'gain-tooltip') + .style('visibility', 'hidden') + .text(formatNumber(parseInt(gainAverage), 10)); + + tooltip + .append('span') + .text('Average Ha gained'); + + var gainLine = graph.append('svg:rect') + .attr('class', 'gain-line') + .attr('x', 0) + .attr('y', Math.abs(y_scale(gainAverage))) + .attr('height', 1) .attr('width', width) - .attr('height', height); - - gainLine.append('svg:line') - .attr('x1', 0) - .attr('x2', width) - .attr('y1', Math.abs(y_scale(gainAverage))) - .attr('y2', Math.abs(y_scale(gainAverage))) - .style('stroke', '#CCC') + .style('stroke', 'transparent') + .style("stroke-width", "5") + .style('fill', '#ccc'); + + gainLine + .on('mousemove', function() { + d3.select('.gain-line') + .style('height', 2) + .style('fill', '#9FBA2B'); + + tooltip + .style('visibility', 'visible') + .style("top", Math.abs(y_scale(gainAverage)) + "px") + .style("left", (d3.mouse(this)[0] - 58) + "px"); + }) + .on('mouseout', function() { + d3.select('.gain-line') + .style('height', 1) + .style('fill', '#ccc'); + + tooltip.style('visibility', 'hidden'); + }) }); }); @@ -450,9 +477,19 @@ gfw.ui.view.CountriesShow = cdb.core.View.extend({ 'FROM gfw2_countries', "WHERE iso = '" + this.country.get('iso') + "'"].join(' '); - d3.json('https://wri-01.cartodb.com/api/v2/sql?q='+sql, function(json) { - // TODO => if percents are 0 - var data = _.pluck(json.rows, 'percent'); + d3.json('https://wri-01.cartodb.com/api/v2/sql?q=' + sql, function(json) { + var data = _.pluck(json.rows, 'percent'), + sumData = _.reduce(data, function(memo, num){ return memo + num; }, 0), + $countryForestType = $('.country-forests-type'); + + if (sumData === 0) { + $countryForestType.find('.left-col').addClass('wide'); + $countryForestType.find('.forest-type-legends').hide(); + $countryForestType.find('.section-content').show(); + return; + } + + $countryForestType.find('.section-content').show(); var width = 225, height = 225, @@ -502,7 +539,6 @@ gfw.ui.view.CountriesShow = cdb.core.View.extend({ $formaAlertsContent = this.$('#formaAlertsContent'), $formaAlertsTitle = this.$('#formaAlertsTitle'); - // Dimensions variables var width = 500, height = 156, @@ -630,7 +666,7 @@ gfw.ui.view.CountriesShow = cdb.core.View.extend({ var cx = d3.mouse(this)[0] + marginLeft, cy = h - y_scale(data[index].alerts) + marginTop, date = new Date(data[index].date), - form_date = config.MONTHNAMES[date.getUTCMonth()] + ' ' + date.getUTCFullYear(); + formattedDate = config.MONTHNAMES[date.getUTCMonth()] + ' ' + date.getUTCFullYear(); marker .attr('cx', cx) @@ -641,7 +677,7 @@ gfw.ui.view.CountriesShow = cdb.core.View.extend({ .attr('x2', d3.mouse(this)[0] + marginLeft); amount.text(formatNumber(data[index].alerts)); - tooltipDate.text(form_date); + tooltipDate.text(formattedDate); tooltip.style("top", "-20px").style("left", (cx - 162) + "px"); } diff --git a/app/assets/stylesheets/application.scss b/app/assets/stylesheets/application.scss index a20ffd8a0a..eea177f569 100644 --- a/app/assets/stylesheets/application.scss +++ b/app/assets/stylesheets/application.scss @@ -411,6 +411,20 @@ strong { #tpglt-nav-wrapper { padding-top: 15px; + min-height: 28px; + width: 100%; + background-color: #e6e6e6; + border-bottom: 1px solid #9b9b9b; + z-index: 1001; + font-size: 12px; + color: #747474; + + div:first-child { + margin: 0 auto; + position: relative; + padding: 0 10px; + max-width: 960px; + } a { color: #333; } } diff --git a/app/assets/stylesheets/countries/show.scss b/app/assets/stylesheets/countries/show.scss index 4b444c4ef4..b15800777d 100644 --- a/app/assets/stylesheets/countries/show.scss +++ b/app/assets/stylesheets/countries/show.scss @@ -7,7 +7,6 @@ width: 100%; .country-header-inner { - height: 350px; $wall-background: "backgrounds/wall_background_alpha.png"; background: #464253; background-image: image-url($wall-background); /* fallback */ @@ -174,7 +173,42 @@ margin-top: 2px; font-size: 13px; } - } + + .gain-tooltip { + position: absolute; + padding: 10px; + width: 110px; + background: $primary-color; + text-align: center; + border-radius: 5px; + font-size: 22px; + color: white; + + span { + font-size: 13px; + color: #60644D; + margin-top: 2px; + display: block; + } + + &:before { + content:''; + display:block; + width:0; + height:0; + position:absolute; + + border-left: 8px solid transparent; + border-right: 8px solid transparent; + border-top: 8px solid $primary-color; + + right: 0; + left: 0; + margin: auto; + bottom: -8px; + } + } + } // loss-gain-graph } .country-sidenav { @@ -268,7 +302,7 @@ width: 100%; position: relative; height: 49px; - + .country-nav { width: 100%; border-bottom: 1px solid #CCCCCC; @@ -362,6 +396,10 @@ .left-col, .right-col { float: left; + + &.wide { + width: 100%; + } } .left-col { @@ -383,13 +421,12 @@ .country-alt { display: inline-block; - width: 100%; + width: 80%; clear: both; padding-top: 20px; color: #666; line-height: 1.4; font-size: 14px; - font-style: italic; } } @@ -507,6 +544,10 @@ } .country-forests-type { + .section-content { + display: none; + } + .forests-type-graph { margin-top: -25px; } @@ -555,6 +596,8 @@ } .country-tenure { + display: none; + .line-graph { font-weight: bold; text-transform: uppercase; diff --git a/app/views/countries/show.html.erb b/app/views/countries/show.html.erb index b124a0bdf3..6fe48b1226 100644 --- a/app/views/countries/show.html.erb +++ b/app/views/countries/show.html.erb @@ -41,7 +41,7 @@

    Total extension

    851 - mha + Mha
    @@ -118,7 +118,7 @@
    -
    Not yet available for this country
    +
    Not available for this country
    From 15d0bfeec4c01fc8889b4ab58d3747c9edc19853 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A1n=20P=C3=A9rez?= Date: Wed, 28 May 2014 18:38:12 +0200 Subject: [PATCH 022/823] umd intensity: changes jquery --- lib/assets/javascripts/gfw/deforestation_tile_layer.js | 4 ++-- lib/assets/javascripts/gfw/ui/umd_options.js.erb | 6 ++---- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/lib/assets/javascripts/gfw/deforestation_tile_layer.js b/lib/assets/javascripts/gfw/deforestation_tile_layer.js index 35371fc55f..3ec189a2d1 100644 --- a/lib/assets/javascripts/gfw/deforestation_tile_layer.js +++ b/lib/assets/javascripts/gfw/deforestation_tile_layer.js @@ -134,13 +134,13 @@ var Deforestation = function() { function filter(image_data, w, h, year_start, year_end) { var components = 4; //rgba var pixel_pos; + var zoom = map.getZoom(); + for(var i=0; i < w; ++i) { for(var j=0; j < h; ++j) { var pixel_pos = (j*w + i) * components; var yearLoss = image_data[pixel_pos]; var intensity = image_data[pixel_pos + 1]; - var zoom = map.getZoom(); - yearLoss = 2000 + yearLoss; if (yearLoss >= year_start && yearLoss < year_end) { diff --git a/lib/assets/javascripts/gfw/ui/umd_options.js.erb b/lib/assets/javascripts/gfw/ui/umd_options.js.erb index 43837afb4e..9adbd466de 100644 --- a/lib/assets/javascripts/gfw/ui/umd_options.js.erb +++ b/lib/assets/javascripts/gfw/ui/umd_options.js.erb @@ -108,11 +108,11 @@ gfw.ui.view.umdoptionsDialog = gfw.ui.view.Widget.extend({ }, _onDragSlider: function() { - this._paintRange($('#canopy_slider').val()) + this._paintRange(document.getElementById('canopy_slider').value) }, _onChangeSlider: function() { - this._paintRange($('#canopy_slider').val()) + this._paintRange(document.getElementById('canopy_slider').value) }, _onClickOption: function(e) { @@ -189,8 +189,6 @@ gfw.ui.view.umdoptionsDialog = gfw.ui.view.Widget.extend({ _onClickApply: function(e) { e && e.preventDefault(); - console.log(this.canopy); - GFW.app. this.hide() }, From 8e56fb2a98e049b819c7df20fc6f9c41ba68f547 Mon Sep 17 00:00:00 2001 From: Pedro Garcia Date: Wed, 28 May 2014 18:57:11 +0200 Subject: [PATCH 023/823] changes --- .../javascripts/countries/views/show.js | 10 +++++---- app/assets/stylesheets/countries/show.scss | 21 ++++++++++++------- app/views/countries/show.html.erb | 3 ++- 3 files changed, 22 insertions(+), 12 deletions(-) diff --git a/app/assets/javascripts/countries/views/show.js b/app/assets/javascripts/countries/views/show.js index eac887eb20..bff7c93de0 100644 --- a/app/assets/javascripts/countries/views/show.js +++ b/app/assets/javascripts/countries/views/show.js @@ -242,10 +242,6 @@ gfw.ui.view.CountriesShow = cdb.core.View.extend({ _.each(tenures, function(tenure, i) { if (tenure['percent'] !== null && tenure['percent'] !== 0) { - if ($('.country-tenure').not(':visible')) { - $('.country-tenure').show(); - } - h += 50; tenures_ord.push({ @@ -255,6 +251,12 @@ gfw.ui.view.CountriesShow = cdb.core.View.extend({ } }); + if (tenures_ord.length === 0) { + $('.country-tenure .coming-soon').show(); + return; + } + console.log('draw tenures'); + var svg = d3.select('.country-tenure .line-graph') .append('svg') .attr('width', 600) diff --git a/app/assets/stylesheets/countries/show.scss b/app/assets/stylesheets/countries/show.scss index b15800777d..aa4664e85c 100644 --- a/app/assets/stylesheets/countries/show.scss +++ b/app/assets/stylesheets/countries/show.scss @@ -430,12 +430,8 @@ } } - .country-alerts { - #formaAlertsContent, - #comingSoonContent { - display: none; - } - + .country-alerts, + .country-tenure { .coming-soon { width: 100%; font-size: 16px; @@ -445,6 +441,17 @@ background: #f2f2f2; color: #666; } + } + + .country-tenure .coming-soon { + display: none; + } + + .country-alerts { + #formaAlertsContent, + #comingSoonContent { + display: none; + } .forma_dropdown-menu { display: none; @@ -596,7 +603,7 @@ } .country-tenure { - display: none; + // display: none; .line-graph { font-weight: bold; diff --git a/app/views/countries/show.html.erb b/app/views/countries/show.html.erb index 6fe48b1226..1447d09b93 100644 --- a/app/views/countries/show.html.erb +++ b/app/views/countries/show.html.erb @@ -83,7 +83,7 @@

    Global Forest Watch does in-depth work in this country.

    Find more information at the Atlas Foreiester Interactif de la République Democratique Du Congo

    @@ -245,6 +245,7 @@
    +
    Not data available
    From e0b66cef810d0ca307609ad07a0f1763067f0e32 Mon Sep 17 00:00:00 2001 From: rschumann Date: Wed, 28 May 2014 19:00:15 +0200 Subject: [PATCH 024/823] add capybara-screenshot --- Gemfile | 1 + Gemfile.lock | 4 +++ spec/features/map_analyze_spec.rb.sample | 37 ++++++++++++++++++++++++ spec/spec_helper.rb | 1 + 4 files changed, 43 insertions(+) create mode 100644 spec/features/map_analyze_spec.rb.sample diff --git a/Gemfile b/Gemfile index 5fa71ea209..a6e3496be3 100644 --- a/Gemfile +++ b/Gemfile @@ -18,6 +18,7 @@ group :development, :test do gem 'rspec-rails', '~> 2.0' gem 'launchy' # this lets us call save_and_open_page to see what's on a page for debugging capybara tests gem 'capybara', '2.0.3' # capybara-webkit works with this version + gem 'capybara-screenshot' gem 'show_me_the_cookies' gem 'factory_girl_rails' gem 'shoulda-matchers' diff --git a/Gemfile.lock b/Gemfile.lock index fe4b86566f..f70839a3da 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -44,6 +44,9 @@ GEM rack-test (>= 0.5.4) selenium-webdriver (~> 2.0) xpath (~> 1.0.0) + capybara-screenshot (0.3.19) + capybara (>= 1.0, < 3) + launchy carrierwave (0.10.0) activemodel (>= 3.2.0) activesupport (>= 3.2.0) @@ -263,6 +266,7 @@ DEPENDENCIES binding_of_caller capistrano capybara (= 2.0.3) + capybara-screenshot carrierwave compass-rails (= 1.1.2) database_cleaner diff --git a/spec/features/map_analyze_spec.rb.sample b/spec/features/map_analyze_spec.rb.sample new file mode 100644 index 0000000000..923c4a2ef8 --- /dev/null +++ b/spec/features/map_analyze_spec.rb.sample @@ -0,0 +1,37 @@ +require 'spec_helper' + +feature 'Map page' do + + background do + visit '/accept_terms' + Capybara.current_session.driver.browser.manage.add_cookie name: true, value: true + end + + scenario 'analyze an area on the map', js: true do + visit '/map' + expect(page).to have_css(".filters", visible: true) + find('#analysis_control').click + page.driver.browser.action.move_to(page.find('#map').native).move_by( 300, 10 ).click.perform + page.driver.browser.action.move_to(page.find('#map').native).move_by( 300, 15 ).click.perform + page.driver.browser.action.move_to(page.find('#map').native).move_by( 350, 15 ).click.perform + page.driver.browser.action.move_to(page.find('#map').native).move_by( 350, 10 ).click.perform + page.driver.browser.action.move_to(page.find('#map').native).move_by( 300, 10 ).click.perform + + -------- + page.driver.browser.execute_script("$(document.elementFromPoint(300, 300)).click();") + page.driver.browser.execute_script("$(document.elementFromPoint(300, 350)).click();") + page.driver.browser.execute_script("$(document.elementFromPoint(350, 350)).click();") + page.driver.browser.execute_script("$(document.elementFromPoint(350, 300)).click();") + page.driver.browser.execute_script("$(document.elementFromPoint(300, 300)).click();") + + --------- + + + + find('.helper_bar').click_on('Done') + expect(page).to have_css(".info_content .stats") + expect(page).to have_content("Total selected area") + expect(page).to have_content("This algorithm approximates the results by sampling the selected area. Results are more accurate at closer zoom levels.") + end + +end \ No newline at end of file diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 04763ab620..2fcf409aad 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -5,6 +5,7 @@ require 'rspec/autorun' require 'capybara/rails' require 'capybara/rspec' +require 'capybara-screenshot/rspec' class Capybara::Selenium::Driver < Capybara::Driver::Base def reset! From 6a2465ebca1d699c6deacff70830b2b24afdb2bf Mon Sep 17 00:00:00 2001 From: Pedro Garcia Date: Thu, 29 May 2014 10:44:12 +0200 Subject: [PATCH 025/823] release candidate 0.2 --- .../javascripts/countries/models/country.js | 7 ----- .../javascripts/countries/views/show.js | 1 - app/assets/stylesheets/countries/show.scss | 20 ++++++++++---- app/controllers/countries_controller.rb | 3 ++- app/views/countries/show.html.erb | 26 +++++++++++++------ 5 files changed, 35 insertions(+), 22 deletions(-) diff --git a/app/assets/javascripts/countries/models/country.js b/app/assets/javascripts/countries/models/country.js index 21d5e91982..c40e4d711e 100644 --- a/app/assets/javascripts/countries/models/country.js +++ b/app/assets/javascripts/countries/models/country.js @@ -14,11 +14,4 @@ gfw.ui.model.Country = cdb.core.Model.extend({ return {fields: response.fields, areas: collection } } -}); - -gfw.ui.model.CountryArea = cdb.core.Model.extend({ -}); - -gfw.ui.collection.CountryAreas = Backbone.Collection.extend({ - model: gfw.ui.model.CountryArea }); \ No newline at end of file diff --git a/app/assets/javascripts/countries/views/show.js b/app/assets/javascripts/countries/views/show.js index bff7c93de0..0740a1e3ab 100644 --- a/app/assets/javascripts/countries/views/show.js +++ b/app/assets/javascripts/countries/views/show.js @@ -255,7 +255,6 @@ gfw.ui.view.CountriesShow = cdb.core.View.extend({ $('.country-tenure .coming-soon').show(); return; } - console.log('draw tenures'); var svg = d3.select('.country-tenure .line-graph') .append('svg') diff --git a/app/assets/stylesheets/countries/show.scss b/app/assets/stylesheets/countries/show.scss index aa4664e85c..4d8f7f7b5b 100644 --- a/app/assets/stylesheets/countries/show.scss +++ b/app/assets/stylesheets/countries/show.scss @@ -431,7 +431,9 @@ } .country-alerts, - .country-tenure { + .country-production, + .country-tenure, + .country-employment { .coming-soon { width: 100%; font-size: 16px; @@ -443,8 +445,10 @@ } } - .country-tenure .coming-soon { - display: none; + .country-tenure { + .coming-soon { + display: none; + } } .country-alerts { @@ -603,8 +607,6 @@ } .country-tenure { - // display: none; - .line-graph { font-weight: bold; text-transform: uppercase; @@ -637,6 +639,14 @@ } } // country-tenure + .country-legislation { + .national-policy-links { + margin: 5px 0 4px 0; + font-size: 14px; + text-decoration: underline; + } + } // country-legislation + .country-carbon-stocks { .carbon-text, .emissions-text { diff --git a/app/controllers/countries_controller.rb b/app/controllers/countries_controller.rb index 7aac477366..31738f8b7c 100644 --- a/app/controllers/countries_controller.rb +++ b/app/controllers/countries_controller.rb @@ -7,7 +7,8 @@ def show not_found unless @country.present? if @country['gva'].present? && @country['gva'] > 0 - @country['gva_percent'] = (@country['gva_percent'] < 0.1) ? number_to_percentage(@country['gva_percent'], precision: 2) : number_to_percentage(@country['gva_percent'], precision: 1) + gva_precision = (@country['gva_percent'] < 0.1) ? 2 : 1 + @country['gva_percent'] = number_to_percentage(@country['gva_percent'], precision: gva_precision) end @employees = @country['employment'] diff --git a/app/views/countries/show.html.erb b/app/views/countries/show.html.erb index 1447d09b93..af043cac26 100644 --- a/app/views/countries/show.html.erb +++ b/app/views/countries/show.html.erb @@ -81,11 +81,9 @@ <% if @country['indepth'].present? %>

    Global Forest Watch does in-depth work in this country.

    -

    Find more information at the Atlas Foreiester Interactif de la République Democratique Du Congo

    - +

    Find more information here

    +
    <% end %> @@ -194,15 +192,18 @@
    + <% if @country['gva'].present? && @country['gva'] > 0 %>

    The forestry sector contributed USD <%= gva_to_human(@country['gva'])%> to the economy in 2006, which is appoximately <%= @country['gva_percent'] %> of the GDP.

    + <% else %> +
    No data available
    + <% end %>
    - <% if @employees.present? && @employees > 0 %>
    + <% if @employees.present? && @employees > 0 %>
    <% if @employees < 1000 %>

    <%= @employees %>
    thousand people are directly employed by the forestry sector, according to 2006 FAO data.

    @@ -231,11 +233,13 @@
    + <% else %> +
    No data available
    + <% end %>
    - <% end %> - +
    @@ -260,6 +264,12 @@
    + <% if @country['national_policy_link'].present? %> + + <% end %> + Are we missing a link?
    From 1d938e29c14ef11be0ce57fc87091b6e6890e13c Mon Sep 17 00:00:00 2001 From: Pedro Garcia Date: Thu, 29 May 2014 17:49:19 +0200 Subject: [PATCH 026/823] share button and hot fixes --- .../images/country-icons-s2a34c6c91a.png | Bin 0 -> 8531 bytes .../images/country-icons-s74837160b5.png | Bin 8221 -> 0 bytes app/assets/images/country-icons/share.png | Bin 0 -> 419 bytes .../javascripts/countries/views/show.js | 8 +++- app/assets/stylesheets/countries/show.scss | 38 +++++++++++++++++- app/views/countries/show.html.erb | 13 ++++-- lib/assets/javascripts/gfw/ui/share.js.erb | 1 + 7 files changed, 55 insertions(+), 5 deletions(-) create mode 100644 app/assets/images/country-icons-s2a34c6c91a.png delete mode 100644 app/assets/images/country-icons-s74837160b5.png create mode 100644 app/assets/images/country-icons/share.png diff --git a/app/assets/images/country-icons-s2a34c6c91a.png b/app/assets/images/country-icons-s2a34c6c91a.png new file mode 100644 index 0000000000000000000000000000000000000000..7c164a4774a8697268d0814ddf8c18445ef9141e GIT binary patch literal 8531 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 literal 0 HcmV?d00001 diff --git a/app/assets/images/country-icons-s74837160b5.png b/app/assets/images/country-icons-s74837160b5.png deleted file mode 100644 index 3892bfe48055aad33673e5a8431c5c22e1d454c1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8221 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 diff --git a/app/assets/images/country-icons/share.png b/app/assets/images/country-icons/share.png new file mode 100644 index 0000000000000000000000000000000000000000..5f573308103bf897d008d8ac8f38b5f29d292ff4 GIT binary patch literal 419 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`EX7WqAsj$Z!;#Vf4nJ za0`Jj^D_
    + +
    - +
    @@ -249,7 +256,7 @@
    -
    Not data available
    +
    No data available
    @@ -460,4 +467,4 @@ <%= render 'shared/sources' %>
    -
    +
    \ No newline at end of file diff --git a/lib/assets/javascripts/gfw/ui/share.js.erb b/lib/assets/javascripts/gfw/ui/share.js.erb index 5467db2586..b153b9dece 100644 --- a/lib/assets/javascripts/gfw/ui/share.js.erb +++ b/lib/assets/javascripts/gfw/ui/share.js.erb @@ -5,6 +5,7 @@ gfw.ui.model.Share = cdb.core.Model.extend({ }); gfw.ui.view.Share = cdb.core.View.extend({ + el: 'body', className: 'share', events: { From f4b141289ffe677ce6f8f0544802093de1820397 Mon Sep 17 00:00:00 2001 From: Pedro Garcia Date: Thu, 29 May 2014 18:22:41 +0200 Subject: [PATCH 027/823] tooltip gainloss --- app/assets/javascripts/countries/views/show.js | 4 ++-- app/assets/stylesheets/countries/show.scss | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/app/assets/javascripts/countries/views/show.js b/app/assets/javascripts/countries/views/show.js index 1bbe952d98..d3094ad8e3 100644 --- a/app/assets/javascripts/countries/views/show.js +++ b/app/assets/javascripts/countries/views/show.js @@ -440,7 +440,7 @@ gfw.ui.view.CountriesShow = cdb.core.View.extend({ tooltip .append('span') - .text('Average Ha gained'); + .text('Average Ha of gain/year'); var gainLine = graph.append('svg:rect') .attr('class', 'gain-line') @@ -456,7 +456,7 @@ gfw.ui.view.CountriesShow = cdb.core.View.extend({ .on('mousemove', function() { d3.select('.gain-line') .style('height', 2) - .style('fill', '#9FBA2B'); + .style('fill', 'white'); tooltip .style('visibility', 'visible') diff --git a/app/assets/stylesheets/countries/show.scss b/app/assets/stylesheets/countries/show.scss index 8ac40f4213..aa22915936 100644 --- a/app/assets/stylesheets/countries/show.scss +++ b/app/assets/stylesheets/countries/show.scss @@ -213,16 +213,16 @@ .gain-tooltip { position: absolute; padding: 10px; - width: 110px; - background: $primary-color; + width: 150px; + background: white; text-align: center; border-radius: 5px; font-size: 22px; - color: white; + color: $primary-color; span { font-size: 13px; - color: #60644D; + color: #524F5C; margin-top: 2px; display: block; } @@ -236,7 +236,7 @@ border-left: 8px solid transparent; border-right: 8px solid transparent; - border-top: 8px solid $primary-color; + border-top: 8px solid white; right: 0; left: 0; From b835c58edaf25c07175c05d6049c527c80c87c83 Mon Sep 17 00:00:00 2001 From: Pedro Garcia Date: Thu, 29 May 2014 19:04:44 +0200 Subject: [PATCH 028/823] hot fixess --- app/assets/javascripts/countries/views/show.js | 2 +- lib/assets/javascripts/gfw/ui/share.js.erb | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/app/assets/javascripts/countries/views/show.js b/app/assets/javascripts/countries/views/show.js index d3094ad8e3..d733ae2b9e 100644 --- a/app/assets/javascripts/countries/views/show.js +++ b/app/assets/javascripts/countries/views/show.js @@ -43,7 +43,7 @@ gfw.ui.view.CountriesShow = cdb.core.View.extend({ _initShare: function() { Share = new gfw.ui.view.Share(); - this.$el.find('.country-show .inner').append(Share.render()); + //this.$el.find('.country-show .inner').append(Share.render()); }, _initSource: function() { diff --git a/lib/assets/javascripts/gfw/ui/share.js.erb b/lib/assets/javascripts/gfw/ui/share.js.erb index b153b9dece..5467db2586 100644 --- a/lib/assets/javascripts/gfw/ui/share.js.erb +++ b/lib/assets/javascripts/gfw/ui/share.js.erb @@ -5,7 +5,6 @@ gfw.ui.model.Share = cdb.core.Model.extend({ }); gfw.ui.view.Share = cdb.core.View.extend({ - el: 'body', className: 'share', events: { From 40544add7f4c633077103e4bcc14cd22a181250e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A1n=20P=C3=A9rez?= Date: Fri, 30 May 2014 09:43:46 +0200 Subject: [PATCH 029/823] timeline modis: fix dates --- lib/assets/javascripts/gfw/ui/analysis.js.erb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/assets/javascripts/gfw/ui/analysis.js.erb b/lib/assets/javascripts/gfw/ui/analysis.js.erb index 9736bb8134..b88a2e3222 100644 --- a/lib/assets/javascripts/gfw/ui/analysis.js.erb +++ b/lib/assets/javascripts/gfw/ui/analysis.js.erb @@ -102,7 +102,7 @@ gfw.ui.view.Analysis = cdb.core.View.extend({ var q1 = config.MONTHNAMES_SHORT[(month_-3 >= 0) ? month_-3 : month_+9].charAt(0).toUpperCase() + config.MONTHNAMES_SHORT[(month_-3 >= 0) ? month_-3 : month_+9].slice(1).toLowerCase() var q2 = config.MONTHNAMES_SHORT[(month_-1 >= 0) ? month_-1 : month_+11].charAt(0).toUpperCase() + config.MONTHNAMES_SHORT[(month_-1 >= 0) ? month_-1 : month_+11].slice(1).toLowerCase() - this.range = '&date='+year_+'-'+month_; + this.range = '&date='+year_+'-'+month_+'-15'; this.info.model.set({ 'subtitle': q1+"-"+year_+' to '+q2+'-'+year_ }); if (this.info.model.get('hidden')) return; From b8e59758309de2d85f3a8a64f8e63c5c9c3a2743 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A1n=20P=C3=A9rez?= Date: Fri, 30 May 2014 10:04:09 +0200 Subject: [PATCH 030/823] timeline fires: filter confidence --- lib/assets/javascripts/gfw/gfw_lib.js.erb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/assets/javascripts/gfw/gfw_lib.js.erb b/lib/assets/javascripts/gfw/gfw_lib.js.erb index 77da7987f6..b11d898ac4 100644 --- a/lib/assets/javascripts/gfw/gfw_lib.js.erb +++ b/lib/assets/javascripts/gfw/gfw_lib.js.erb @@ -1118,7 +1118,7 @@ GFW.modules.app = function(gfw) { month = (end_day.getUTCMonth() + 1) < 10 ? '0' + parseInt(end_day.getUTCMonth() + 1, 10) : parseInt(end_day.getUTCMonth(), 10), day = end_day.getUTCDate() < 10 ? '0' + end_day.getUTCDate() : end_day.getUTCDate(); var query = [ - "SELECT * FROM global_7d where acq_date > '" + year + "-" + month + "-" + day + "'" + "SELECT * FROM global_7d where acq_date > '" + year + "-" + month + "-" + day + "' AND CAST(confidence AS INT)> 30" ].join(' '); this.baseLayer.setQuery(query); From b7f267eb7a7825e20984f72d3700b90b0473cf94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A1n=20P=C3=A9rez?= Date: Fri, 30 May 2014 13:57:56 +0200 Subject: [PATCH 031/823] analysis: correct spinner on second analysis --- lib/assets/javascripts/gfw/ui/analysis.js.erb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/assets/javascripts/gfw/ui/analysis.js.erb b/lib/assets/javascripts/gfw/ui/analysis.js.erb index b88a2e3222..a3289cc4c1 100644 --- a/lib/assets/javascripts/gfw/ui/analysis.js.erb +++ b/lib/assets/javascripts/gfw/ui/analysis.js.erb @@ -349,6 +349,9 @@ gfw.ui.view.Analysis = cdb.core.View.extend({ }, _getAlertCount: function() { + var $spinner = this.$el.find('.stats .spinner') + if (!$spinner.is('visible')) $spinner.fadeIn(250); + var that = this; var area = this.model.get('area'), @@ -431,6 +434,7 @@ gfw.ui.view.Analysis = cdb.core.View.extend({ this.$el.find('.analysis_info').show(); } } + executeAjax(this.alertsUrl, {}, { success: _.bind(function(data) { var baselayer = config.BASELAYER, From c05e746b6a838c7188d133055af01bce29fd444a Mon Sep 17 00:00:00 2001 From: Pedro Garcia Date: Fri, 30 May 2014 14:07:14 +0200 Subject: [PATCH 032/823] select area --- .../javascripts/countries/models/country.js | 28 ++- .../javascripts/countries/views/show.js | 160 +++++++++++++----- app/assets/stylesheets/countries/show.scss | 14 +- app/views/countries/show.html.erb | 8 +- 4 files changed, 157 insertions(+), 53 deletions(-) diff --git a/app/assets/javascripts/countries/models/country.js b/app/assets/javascripts/countries/models/country.js index c40e4d711e..e534927a2a 100644 --- a/app/assets/javascripts/countries/models/country.js +++ b/app/assets/javascripts/countries/models/country.js @@ -1,17 +1,35 @@ gfw.ui.model.Country = cdb.core.Model.extend({ + initialize: function() { + }, + url: function() { - return "http://wri-01.cartodb.com/api/v2/sql?q=SELECT iso,id_1,name_1 FROM gadm2_provinces where iso = '" + this.get('iso') + "' order by id_1 asc"; + return "http://wri-01.cartodb.com/api/v2/sql?q=SELECT cartodb_id, iso, id_1, name_1, bounds FROM gadm_1_all where iso = '" + this.get('iso') + "' order by id_1 asc"; }, parse: function(response, options) { - var collection = new gfw.ui.collection.CountryAreas() + var collection = new gfw.ui.collection.CountryAreas(); _.each(response.rows, function(row) { - collection.add(new gfw.ui.model.CountryArea(row)) + var bounds = JSON.parse(row.bounds), + geojson = L.geoJson(bounds); + + collection.add(new gfw.ui.model.CountryArea({ + cartodb_id: row.cartodb_id, + iso: row.iso, + id_1: row.id_1, + name_1: row.name_1, + bounds: geojson.getBounds() + })) }); - return {fields: response.fields, areas: collection } + return {fields: response.fields, areas: collection} } -}); \ No newline at end of file +}); + +gfw.ui.collection.CountryAreas = Backbone.Collection.extend({ + model: gfw.ui.model.CountryArea +}); + +gfw.ui.model.CountryArea = cdb.core.Model.extend({}); \ No newline at end of file diff --git a/app/assets/javascripts/countries/views/show.js b/app/assets/javascripts/countries/views/show.js index d733ae2b9e..39b884198d 100644 --- a/app/assets/javascripts/countries/views/show.js +++ b/app/assets/javascripts/countries/views/show.js @@ -11,27 +11,19 @@ gfw.ui.view.CountriesShow = cdb.core.View.extend({ var self = this; _.bindAll(this, '_positionScroll'); + // Cache this.$nav = this.$('.country-nav'); this.$indepth = this.$('.country-indepth'); - // this.$areaSelector = this.$('#areaSelector'); + this.$areaSelector = this.$('#areaSelector'); + this.$map = this.$('.map'); + // Models this.country = new gfw.ui.model.Country({ iso: this.options.iso }); - // this._setAreaSelector(); + // Initialize modules + this._initMap(); this._stickynav(); - this._initViews(); - - var sql = new cartodb.SQL({ user: 'wri-01' }); - sql.execute("SELECT bounds FROM country_mask WHERE code = '" + this.country.get('iso') + "'") - .done(function(data) { - var bounds = JSON.parse(data.rows[0].bounds), - geojson = L.geoJson(bounds); - - self._initMap(geojson.getBounds()); - }); - }, - - _initViews: function() { + this._setAreaSelector(); this._initSource(); this._drawLossAndGain(); this._drawTenure(); @@ -56,7 +48,7 @@ gfw.ui.view.CountriesShow = cdb.core.View.extend({ var source = $(e.target).closest('.info').attr('data-source'); - ga('send', 'event', 'SourceWindow', 'Open', source); + // ga('send', 'event', 'SourceWindow', 'Open', source); this.sourceWindow.show(source).addScroll(); }, @@ -97,45 +89,97 @@ gfw.ui.view.CountriesShow = cdb.core.View.extend({ _.each(model.get('areas').models, function(area) { self.$areaSelector.append('') }); - }, - error: function() {} + } }); }, _onSelectArea: function() { - var areaName = this.$areaSelector.val(); - var area = this.country.get('areas').where({ name_1: areaName })[0]; + var self = this, + areaName = this.$areaSelector.val(), + area = this.country.get('areas').where({ name_1: areaName })[0]; + if (area) { + this._displayArea(area); + } else { + this._displayCountry(); + } }, - _initMap: function(bounds) { + _initMap: function() { + var self = this, + sql = new cartodb.SQL({ user: 'wri-01' }); + + sql.execute("SELECT bounds FROM country_mask WHERE code = '" + this.country.get('iso') + "'") + .done(function(data) { + var geojson = L.geoJson(JSON.parse(data.rows[0].bounds)), + bounds = geojson.getBounds(); + + self.country.set('bounds', bounds); + self._renderMap(); + }); + }, + + _renderMap: function() { var self = this; + // Map & layers + this.cartodbLayer = {}; + this.forestLayer = {}; + this.map = {}; + this.map = new L.Map('countryMap', { center: [0, 0], - maxBounds: bounds, + maxBounds: this.country.get('bounds'), zoom: 3, zoomControl: false, dragging: false, touchZoom: false, doubleClickZoom: false, scrollWheelZoom: false, - attributionControl: false + attributionControl: false, + zoomAnimation: false, + fadeAnimation: false }); - this.map.fitBounds(bounds); - - // set forest cover layer - var tileLayer = L.tileLayer('http://earthengine.google.org/static/hansen_2013/tree_alpha/{z}/{x}/{y}.png', { + // Set forest-cover layer + this.forestLayer = L.tileLayer('http://earthengine.google.org/static/hansen_2013/tree_alpha/{z}/{x}/{y}.png', { opacity: 0 }).addTo(this.map); - // set country layer + // Set country layer cartodb.createLayer(this.map, { user_name: 'wri-01', type: 'cartodb', cartodb_logo: false, sublayers: [{ - sql: "SELECT * FROM country_mask", + sql: "SELECT * FROM country_mask" + }, { + sql: "SELECT * FROM gadm_1_all" + }] + }) + .addTo(this.map) + .done(function(layer) { + self.cartodbLayer = layer; + self._displayCountry(); + + self.cartodbLayer.on('loading' , function() { + self.$map.removeClass('loaded'); + self.forestLayer.setOpacity(0); + }); + + self.cartodbLayer.on('load' , function() { + setTimeout(function() { + self.$map.addClass('loaded'); + self.forestLayer.setOpacity(1); + }, 100); + }); + }); + }, + + _displayCountry: function() { + this.map.fitBounds(this.country.get('bounds')); + this.cartodbLayer.getSubLayer(1).hide(); + this.cartodbLayer.getSubLayer(0) + .set({ cartocss: "\ #country_mask {\ polygon-fill: #373442;\ @@ -145,20 +189,55 @@ gfw.ui.view.CountriesShow = cdb.core.View.extend({ line-opacity: 1;\ }\ #country_mask[code='" + this.country.get('iso') + "'] {\ - polygon-fill: #FF6600;\ polygon-opacity: 0;\ line-color: #73707D;\ line-width: 1;\ line-opacity: 1;\ }" - }] - }) - .addTo(this.map) - .done(function(layer) { - setTimeout(function() { - tileLayer.setOpacity(1) - }, 600); - }); + }); + }, + + _displayArea: function(area) { + this.map.fitBounds(area.get('bounds')); + this.cartodbLayer.getSubLayer(1).show(); + this.cartodbLayer.getSubLayer(1) + .set({ + cartocss: "\ + #gadm_1_all {\ + polygon-fill: #373442;\ + polygon-opacity: 1;\ + line-color: #373442;\ + line-width: 1;\ + line-opacity: 1;\ + [cartodb_id=" + area.get('cartodb_id') + "]{\ + polygon-opacity: 0;\ + }\ + ::active[cartodb_id=" + area.get('cartodb_id') + "] {\ + polygon-opacity: 0;\ + line-color: #73707D;\ + line-width: 1;\ + line-opacity: 1;\ + }\ + }" + }); + + this.cartodbLayer.getSubLayer(0) + .set({ + cartocss: "\ + #country_mask {\ + polygon-fill: #373442;\ + polygon-opacity: 1;\ + line-color: #373442;\ + line-width: 1;\ + line-opacity: 1;\ + }\ + #country_mask[code='" + this.country.get('iso') + "'] {\ + polygon-opacity: 0;\ + line-color: #373442;\ + line-width: 1;\ + line-opacity: 1;\ + }" + }); }, _positionScroll: function() { @@ -693,6 +772,11 @@ gfw.ui.view.CountriesShow = cdb.core.View.extend({ }); }, + _toggleMapBg: function() { + console.log('togggle map'); + this.$map.toggleClass('loaded'); + }, + _openDropdown: function(e) { e.preventDefault(); }, diff --git a/app/assets/stylesheets/countries/show.scss b/app/assets/stylesheets/countries/show.scss index aa22915936..1c7635068d 100644 --- a/app/assets/stylesheets/countries/show.scss +++ b/app/assets/stylesheets/countries/show.scss @@ -77,7 +77,7 @@ background: transparent; color: #BEBCC2; -webkit-appearance: none; - padding: 9px 15px; + padding: 10px 15px; width: 100%; &:focus { @@ -119,9 +119,11 @@ .map { width: 40%; height: 100%; - - &.leaflet-container { - background: transparent; + background: transparent; + cursor: default; + + &.loaded { + background: #464253; } } @@ -145,11 +147,11 @@ font-weight: bold; } - .total-extension { + .total-area { margin-top: 18px; } - .total-extension .amount { + .total-area .amount { font-size: 29px; } } diff --git a/app/views/countries/show.html.erb b/app/views/countries/show.html.erb index 488e642e47..ad517c1e90 100644 --- a/app/views/countries/show.html.erb +++ b/app/views/countries/show.html.erb @@ -29,11 +29,11 @@

    <%= @country['name'] %>

    - +
    @@ -45,8 +45,8 @@ <%= extent_to_human(@country['extent'], format: '%n') %> <%= extent_to_human(@country['extent'], format: '%u', shortUnit: true) %>
    -
    -

    Total extension

    +
    +

    Total area

    851 Mha
    From 89424c5937fa9ec737a28e5f0a3f2054c122df1b Mon Sep 17 00:00:00 2001 From: Pedro Garcia Date: Fri, 30 May 2014 18:40:40 +0200 Subject: [PATCH 033/823] routes --- .../javascripts/countries/views/header.js | 365 ++++++++++++++++++ .../javascripts/countries/views/show.js | 306 +-------------- app/assets/stylesheets/countries/show.scss | 28 ++ app/views/countries/_header.html.erb | 66 ++++ app/views/countries/show.html.erb | 85 +--- config/routes.rb | 3 + 6 files changed, 474 insertions(+), 379 deletions(-) create mode 100644 app/assets/javascripts/countries/views/header.js create mode 100644 app/views/countries/_header.html.erb diff --git a/app/assets/javascripts/countries/views/header.js b/app/assets/javascripts/countries/views/header.js new file mode 100644 index 0000000000..72c11a1a57 --- /dev/null +++ b/app/assets/javascripts/countries/views/header.js @@ -0,0 +1,365 @@ +gfw.ui.model.CountryHeaderStatus = cdb.core.Model.extend({}); + +gfw.ui.view.CountryHeader = cdb.core.View.extend({ + + el: $('.country-header'), + + events: { + 'change #areaSelector': '_onSelectArea', + 'click .selector-remove': '_navigateCountry' + }, + + initialize: function(options) { + _.extend(this, options); + var self = this; + + // Cache + this.$areaSelector = this.$('#areaSelector'); + this.$selectorRemove = this.$('.selector-remove'); + this.$map = this.$('.map'); + + var Router = Backbone.Router.extend({ + routes: { + 'country/:id': 'loadCountry', + 'country/:id/:areaId': 'loadArea' + }, + + initialize: function() { + self._drawLossAndGain(); + }, + + loadArea: function(countryId, areaId) { + var area = self.country.get('areas').where({ id_1: Number(areaId) })[0]; + self.area = area; + self._setAreaSelector(); + if (!self.map) { + self._initMap(function() { + self._displayArea(area); + }); + } else { + self._displayArea(area); + } + }, + + loadCountry: function(countryId) { + self._setAreaSelector(); + if (!self.map) { + self._initMap(function() { + self._displayCountry(); + }); + } else { + self._displayCountry(); + } + } + + }); + + + this.country.fetch({ + success: function() { + self.router = new Router(); + Backbone.history.start({pushState: true}); + } + }); + }, + + _setAreaSelector: function() { + var self = this; + + _.each(this.country.get('areas').models, function(area) { + if (self.area == area) { + self.$areaSelector.append('') + } else { + self.$areaSelector.append('') + } + }); + }, + + _onSelectArea: function() { + var self = this, + areaName = this.$areaSelector.val(), + area = this.country.get('areas').where({ name_1: areaName })[0]; + + this.router.navigate('country/' + this.country.get('iso') + '/' + String(area.get('id_1')), {trigger: true}); + }, + + _navigateCountry: function() { + this.router.navigate('country/' + this.country.get('iso'), {trigger: true}); + }, + + _initMap: function(callback) { + var self = this, + sql = new cartodb.SQL({ user: 'wri-01' }); + + sql.execute("SELECT bounds FROM country_mask WHERE code = '" + this.country.get('iso') + "'") + .done(function(data) { + var geojson = L.geoJson(JSON.parse(data.rows[0].bounds)), + bounds = geojson.getBounds(); + + self.country.set('bounds', bounds); + self._renderMap(callback); + }); + }, + + _renderMap: function(callback) { + var self = this; + + // Map & layers + this.cartodbLayer = {}; + this.forestLayer = {}; + this.map = {}; + + this.map = new L.Map('countryMap', { + center: [0, 0], + maxBounds: this.country.get('bounds'), + zoom: 3, + zoomControl: false, + dragging: false, + touchZoom: false, + doubleClickZoom: false, + scrollWheelZoom: false, + attributionControl: false, + zoomAnimation: false, + fadeAnimation: false, + }); + + // Set forest-cover layer + this.forestLayer = L.tileLayer('http://earthengine.google.org/static/hansen_2013/tree_alpha/{z}/{x}/{y}.png', { + opacity: 0 + }).addTo(this.map); + + // Set country layer + cartodb.createLayer(this.map, { + user_name: 'wri-01', + type: 'cartodb', + cartodb_logo: false, + sublayers: [{ + sql: "SELECT * FROM country_mask" + }, { + sql: "SELECT * FROM gadm_1_all" + }] + }) + .addTo(this.map) + .done(function(layer) { + self.cartodbLayer = layer; + + callback(); + + self.cartodbLayer.on('loading' , function() { + self.$map.removeClass('loaded'); + self.forestLayer.setOpacity(0); + }); + + self.cartodbLayer.on('load' , function() { + setTimeout(function() { + self.$map.addClass('loaded'); + self.forestLayer.setOpacity(1); + }, 150); + }); + }); + }, + + _displayCountry: function() { + this.$selectorRemove.hide(); + this.$areaSelector.val(''); + this.map.fitBounds(this.country.get('bounds')); + this.cartodbLayer.getSubLayer(1).hide(); + this.cartodbLayer.getSubLayer(0) + .set({ + cartocss: "\ + #country_mask {\ + polygon-fill: #373442;\ + polygon-opacity: 1;\ + line-color: #373442;\ + line-width: 1;\ + line-opacity: 1;\ + }\ + #country_mask[code='" + this.country.get('iso') + "'] {\ + polygon-opacity: 0;\ + line-color: #73707D;\ + line-width: 1;\ + line-opacity: 1;\ + }" + }); + }, + + _displayArea: function(area) { + this.$selectorRemove.show(); + this.map.fitBounds(area.get('bounds'), {reset: true}); + this.cartodbLayer.getSubLayer(1) + .set({ + cartocss: "\ + #gadm_1_all {\ + polygon-fill: #373442;\ + polygon-opacity: 1;\ + line-color: #373442;\ + line-width: 1;\ + line-opacity: 1;\ + [cartodb_id=" + area.get('cartodb_id') + "]{\ + polygon-opacity: 0;\ + }\ + ::active[cartodb_id=" + area.get('cartodb_id') + "] {\ + polygon-opacity: 0;\ + line-color: #73707D;\ + line-width: 1;\ + line-opacity: 1;\ + }\ + }" + }) + .show(); + + this.cartodbLayer.getSubLayer(0) + .set({ + cartocss: "\ + #country_mask {\ + polygon-fill: #373442;\ + polygon-opacity: 1;\ + line-color: #373442;\ + line-width: 1;\ + line-opacity: 1;\ + }\ + #country_mask[code='" + this.country.get('iso') + "'] {\ + polygon-opacity: 0;\ + line-color: #373442;\ + line-width: 1;\ + line-opacity: 1;\ + }" + }); + }, + + _drawLossAndGain: function() { + var sql = "SELECT year, loss_gt_0 loss FROM umd WHERE iso='" + this.country.get('iso') + "' ORDER BY year ASC", + that = this; + + var $graph = this.$('.loss-gain-graph'), + $comingSoon = $graph.find('.coming-soon'), + $amount = $graph.find('.graph-amount'), + $date = $graph.find('.graph-date'); + + var width = 200, + height = 90, + h = 90, // maxHeight + radius = width / 2; + + var graph = d3.select('.loss-gain-graph .graph') + .append('svg:svg') + .attr('width', width) + .attr('height', height); + + d3.json('https://wri-01.cartodb.com/api/v2/sql?q=' + sql, function(json) { + if (json) { + $graph.removeClass('ghost'); + var data = json.rows; + } else { + $comingSoon.show(); + return; + } + + var data_ = []; + + _.each(data, function(val, key) { + if (val.year >= 2001) { + data_.push({ + 'year': val.year, + 'value': val.loss//eval('val.' + options.dataset) + }); + } + }); + + $amount.html('' + formatNumber(parseInt(data_[data_.length - 1].value, 10)) + ''); + $date.html('Hectares lost in ' + data_[data_.length - 1].year); + + var marginLeft = 5, + marginTop = 0; + + var y_scale = d3.scale.linear() + .domain([0, d3.max(data_, function(d) { return parseFloat(d.value); })]) + .range([height, marginTop * 2]); + + var barWidth = 16; + + var bar = graph.selectAll('g') + .data(data_) + .enter() + .append('g') + .attr('transform', function(d, i) { return 'translate(' + (marginLeft + i * barWidth) + ',' + -marginTop + ')'; }); + + bar.append('svg:rect') + .attr('class', 'bar') + .attr('y', function(d) { return y_scale(d.value); }) + .attr('height', function(d) { return height - y_scale(d.value); }) + .attr('width', barWidth - 2) + .style('fill', function(d, i) { + if (i === 11) { + return '#9FBA2B'; + } else { + return '#524F5C'; + } + }) + .on('mouseover', function(d) { + d3.selectAll('.bar').style('fill', '#524F5C'); + d3.select(this).style('fill', '#9FBA2B'); + + $amount.html('' + formatNumber(parseInt(d.value, 10)) + ''); + $date.html('Hectares lost in ' + d.year); + }); + + // Draw gain line + var sql = 'SELECT y2001_y2012 as gain, (SELECT SUM('; + + for(var y = 2001; y < 2012; y++) { + sql += 'y'+y+' + '; + } + + sql += ['y2012) FROM countries_loss', + "WHERE iso = '" + that.country.get('iso') + "') as loss", + 'FROM countries_gain', + "WHERE iso = '" + that.country.get('iso') + "'"].join(' '); + + d3.json('https://wri-01.cartodb.com/api/v2/sql?q=' + encodeURIComponent(sql), function(json) { + var gainAverage = json.rows[0].gain / 12; + + var tooltip = d3.select('.loss-gain-graph') + .append('div') + .attr('class', 'gain-tooltip') + .style('visibility', 'hidden') + .text(formatNumber(parseInt(gainAverage), 10)); + + tooltip + .append('span') + .text('Average Ha of gain/year'); + + var gainLine = graph.append('svg:rect') + .attr('class', 'gain-line') + .attr('x', 0) + .attr('y', Math.abs(y_scale(gainAverage))) + .attr('height', 1) + .attr('width', width) + .style('stroke', 'transparent') + .style("stroke-width", "5") + .style('fill', '#ccc'); + + gainLine + .on('mousemove', function() { + d3.select('.gain-line') + .style('height', 2) + .style('fill', 'white'); + + tooltip + .style('visibility', 'visible') + .style("top", Math.abs(y_scale(gainAverage)) + "px") + .style("left", (d3.mouse(this)[0] - 58) + "px"); + }) + .on('mouseout', function() { + d3.select('.gain-line') + .style('height', 1) + .style('fill', '#ccc'); + + tooltip.style('visibility', 'hidden'); + }) + }); + + }); + } + +}); \ No newline at end of file diff --git a/app/assets/javascripts/countries/views/show.js b/app/assets/javascripts/countries/views/show.js index 39b884198d..6a9a482432 100644 --- a/app/assets/javascripts/countries/views/show.js +++ b/app/assets/javascripts/countries/views/show.js @@ -4,7 +4,6 @@ gfw.ui.view.CountriesShow = cdb.core.View.extend({ events: { 'click .info': '_openSource', 'click .forma_dropdown-link': '_openDropdown', - 'change #areaSelector': '_onSelectArea' }, initialize: function() { @@ -14,18 +13,14 @@ gfw.ui.view.CountriesShow = cdb.core.View.extend({ // Cache this.$nav = this.$('.country-nav'); this.$indepth = this.$('.country-indepth'); - this.$areaSelector = this.$('#areaSelector'); - this.$map = this.$('.map'); // Models this.country = new gfw.ui.model.Country({ iso: this.options.iso }); // Initialize modules - this._initMap(); + this.headerView = new gfw.ui.view.CountryHeader({country: this.country}); this._stickynav(); - this._setAreaSelector(); this._initSource(); - this._drawLossAndGain(); this._drawTenure(); this._drawForestsType(); this._drawFormaAlerts(); @@ -81,165 +76,6 @@ gfw.ui.view.CountriesShow = cdb.core.View.extend({ }); }, - _setAreaSelector: function() { - var self = this; - - this.country.fetch({ - success: function(model, response, options) { - _.each(model.get('areas').models, function(area) { - self.$areaSelector.append('') - }); - } - }); - }, - - _onSelectArea: function() { - var self = this, - areaName = this.$areaSelector.val(), - area = this.country.get('areas').where({ name_1: areaName })[0]; - if (area) { - this._displayArea(area); - } else { - this._displayCountry(); - } - }, - - _initMap: function() { - var self = this, - sql = new cartodb.SQL({ user: 'wri-01' }); - - sql.execute("SELECT bounds FROM country_mask WHERE code = '" + this.country.get('iso') + "'") - .done(function(data) { - var geojson = L.geoJson(JSON.parse(data.rows[0].bounds)), - bounds = geojson.getBounds(); - - self.country.set('bounds', bounds); - self._renderMap(); - }); - }, - - _renderMap: function() { - var self = this; - - // Map & layers - this.cartodbLayer = {}; - this.forestLayer = {}; - this.map = {}; - - this.map = new L.Map('countryMap', { - center: [0, 0], - maxBounds: this.country.get('bounds'), - zoom: 3, - zoomControl: false, - dragging: false, - touchZoom: false, - doubleClickZoom: false, - scrollWheelZoom: false, - attributionControl: false, - zoomAnimation: false, - fadeAnimation: false - }); - - // Set forest-cover layer - this.forestLayer = L.tileLayer('http://earthengine.google.org/static/hansen_2013/tree_alpha/{z}/{x}/{y}.png', { - opacity: 0 - }).addTo(this.map); - - // Set country layer - cartodb.createLayer(this.map, { - user_name: 'wri-01', - type: 'cartodb', - cartodb_logo: false, - sublayers: [{ - sql: "SELECT * FROM country_mask" - }, { - sql: "SELECT * FROM gadm_1_all" - }] - }) - .addTo(this.map) - .done(function(layer) { - self.cartodbLayer = layer; - self._displayCountry(); - - self.cartodbLayer.on('loading' , function() { - self.$map.removeClass('loaded'); - self.forestLayer.setOpacity(0); - }); - - self.cartodbLayer.on('load' , function() { - setTimeout(function() { - self.$map.addClass('loaded'); - self.forestLayer.setOpacity(1); - }, 100); - }); - }); - }, - - _displayCountry: function() { - this.map.fitBounds(this.country.get('bounds')); - this.cartodbLayer.getSubLayer(1).hide(); - this.cartodbLayer.getSubLayer(0) - .set({ - cartocss: "\ - #country_mask {\ - polygon-fill: #373442;\ - polygon-opacity: 1;\ - line-color: #373442;\ - line-width: 1;\ - line-opacity: 1;\ - }\ - #country_mask[code='" + this.country.get('iso') + "'] {\ - polygon-opacity: 0;\ - line-color: #73707D;\ - line-width: 1;\ - line-opacity: 1;\ - }" - }); - }, - - _displayArea: function(area) { - this.map.fitBounds(area.get('bounds')); - this.cartodbLayer.getSubLayer(1).show(); - this.cartodbLayer.getSubLayer(1) - .set({ - cartocss: "\ - #gadm_1_all {\ - polygon-fill: #373442;\ - polygon-opacity: 1;\ - line-color: #373442;\ - line-width: 1;\ - line-opacity: 1;\ - [cartodb_id=" + area.get('cartodb_id') + "]{\ - polygon-opacity: 0;\ - }\ - ::active[cartodb_id=" + area.get('cartodb_id') + "] {\ - polygon-opacity: 0;\ - line-color: #73707D;\ - line-width: 1;\ - line-opacity: 1;\ - }\ - }" - }); - - this.cartodbLayer.getSubLayer(0) - .set({ - cartocss: "\ - #country_mask {\ - polygon-fill: #373442;\ - polygon-opacity: 1;\ - line-color: #373442;\ - line-width: 1;\ - line-opacity: 1;\ - }\ - #country_mask[code='" + this.country.get('iso') + "'] {\ - polygon-opacity: 0;\ - line-color: #373442;\ - line-width: 1;\ - line-opacity: 1;\ - }" - }); - }, - _positionScroll: function() { var h_min = $('.country-alerts').offset().top - 48, h_max = $('.country-conventions').offset().top - 46; @@ -419,141 +255,6 @@ gfw.ui.view.CountriesShow = cdb.core.View.extend({ }); }, - _drawLossAndGain: function() { - var sql = "SELECT year, loss_gt_0 loss FROM umd WHERE iso='" + this.country.get('iso') + "' ORDER BY year ASC", - that = this; - - var $graph = this.$('.loss-gain-graph'), - $comingSoon = $graph.find('.coming-soon'), - $amount = $graph.find('.graph-amount'), - $date = $graph.find('.graph-date'); - - var width = 200, - height = 90, - h = 90, // maxHeight - radius = width / 2; - - var graph = d3.select('.loss-gain-graph .graph') - .append('svg:svg') - .attr('width', width) - .attr('height', height); - - d3.json('https://wri-01.cartodb.com/api/v2/sql?q=' + sql, function(json) { - if (json) { - $graph.removeClass('ghost'); - var data = json.rows; - } else { - $comingSoon.show(); - return; - } - - var data_ = []; - - _.each(data, function(val, key) { - if (val.year >= 2001) { - data_.push({ - 'year': val.year, - 'value': val.loss//eval('val.' + options.dataset) - }); - } - }); - - $amount.html('' + formatNumber(parseInt(data_[data_.length - 1].value, 10)) + ''); - $date.html('Hectares lost in ' + data_[data_.length - 1].year); - - var marginLeft = 5, - marginTop = 0; - - var y_scale = d3.scale.linear() - .domain([0, d3.max(data_, function(d) { return parseFloat(d.value); })]) - .range([height, marginTop * 2]); - - var barWidth = 16; - - var bar = graph.selectAll('g') - .data(data_) - .enter() - .append('g') - .attr('transform', function(d, i) { return 'translate(' + (marginLeft + i * barWidth) + ',' + -marginTop + ')'; }); - - bar.append('svg:rect') - .attr('class', 'bar') - .attr('y', function(d) { return y_scale(d.value); }) - .attr('height', function(d) { return height - y_scale(d.value); }) - .attr('width', barWidth - 2) - .style('fill', function(d, i) { - if (i === 11) { - return '#9FBA2B'; - } else { - return '#524F5C'; - } - }) - .on('mouseover', function(d) { - d3.selectAll('.bar').style('fill', '#524F5C'); - d3.select(this).style('fill', '#9FBA2B'); - - $amount.html('' + formatNumber(parseInt(d.value, 10)) + ''); - $date.html('Hectares lost in ' + d.year); - }); - - // Draw gain line - var sql = 'SELECT y2001_y2012 as gain, (SELECT SUM('; - - for(var y = 2001; y < 2012; y++) { - sql += 'y'+y+' + '; - } - - sql += ['y2012) FROM countries_loss', - "WHERE iso = '" + that.country.get('iso') + "') as loss", - 'FROM countries_gain', - "WHERE iso = '" + that.country.get('iso') + "'"].join(' '); - - d3.json('https://wri-01.cartodb.com/api/v2/sql?q=' + encodeURIComponent(sql), function(json) { - var gainAverage = json.rows[0].gain / 12; - - var tooltip = d3.select('.loss-gain-graph') - .append('div') - .attr('class', 'gain-tooltip') - .style('visibility', 'hidden') - .text(formatNumber(parseInt(gainAverage), 10)); - - tooltip - .append('span') - .text('Average Ha of gain/year'); - - var gainLine = graph.append('svg:rect') - .attr('class', 'gain-line') - .attr('x', 0) - .attr('y', Math.abs(y_scale(gainAverage))) - .attr('height', 1) - .attr('width', width) - .style('stroke', 'transparent') - .style("stroke-width", "5") - .style('fill', '#ccc'); - - gainLine - .on('mousemove', function() { - d3.select('.gain-line') - .style('height', 2) - .style('fill', 'white'); - - tooltip - .style('visibility', 'visible') - .style("top", Math.abs(y_scale(gainAverage)) + "px") - .style("left", (d3.mouse(this)[0] - 58) + "px"); - }) - .on('mouseout', function() { - d3.select('.gain-line') - .style('height', 1) - .style('fill', '#ccc'); - - tooltip.style('visibility', 'hidden'); - }) - }); - - }); - }, - _drawForestsType: function() { var sql = ["SELECT unnest(array['forest_regenerated', 'forest_primary', 'forest_planted'])", 'AS type, unnest(array[COALESCE(forest_regenerated, 0),', @@ -772,11 +473,6 @@ gfw.ui.view.CountriesShow = cdb.core.View.extend({ }); }, - _toggleMapBg: function() { - console.log('togggle map'); - this.$map.toggleClass('loaded'); - }, - _openDropdown: function(e) { e.preventDefault(); }, diff --git a/app/assets/stylesheets/countries/show.scss b/app/assets/stylesheets/countries/show.scss index 1c7635068d..71d7d90473 100644 --- a/app/assets/stylesheets/countries/show.scss +++ b/app/assets/stylesheets/countries/show.scss @@ -68,6 +68,7 @@ border-radius: 40px; height: 39px; width: 310px; + position: relative; } select { @@ -84,6 +85,33 @@ outline: 0; } } + + .selector-arrow { + border-color: #73707D transparent; + border-style: solid; + border-width: 6px 5px 0 5px; + width: 0; + height: 0; + top: 50%; + right: 20px; + margin-top: -3px; + position: absolute; + pointer-events: none; + } + + .selector-remove { + padding: 5px; + color: #73707D; + font-size: 13px; + top: 9px; + right: 35px; + position: absolute; + cursor: pointer; + display: none; + &:hover { + color: lighten(#73707D, 10); + } + } } // country-title .country-details { diff --git a/app/views/countries/_header.html.erb b/app/views/countries/_header.html.erb new file mode 100644 index 0000000000..5e814db619 --- /dev/null +++ b/app/views/countries/_header.html.erb @@ -0,0 +1,66 @@ +
    +
    + + +
    +
    +

    <%= @country['name'] %>

    +
    + +
    +
    +
    +
    + +
    +
    +
    +
    +
    +

    Tree cover

    + <%= extent_to_human(@country['extent'], format: '%n') %> + <%= extent_to_human(@country['extent'], format: '%u', shortUnit: true) %> +
    +
    +

    Total area

    + 851 + Mha +
    +
    +
    +

    LOSS AND GAIN (2001 - 2012)

    +
    +
    +
    +
    Not yet available for this country
    +
    +
    + +
    +
    + +
    +
    \ No newline at end of file diff --git a/app/views/countries/show.html.erb b/app/views/countries/show.html.erb index ad517c1e90..1dc7396a35 100644 --- a/app/views/countries/show.html.erb +++ b/app/views/countries/show.html.erb @@ -22,80 +22,17 @@
    -
    -
    - - -
    -
    -

    <%= @country['name'] %>

    -
    - -
    -
    - -
    -
    -
    -
    -
    -

    Tree cover

    - <%= extent_to_human(@country['extent'], format: '%n') %> - <%= extent_to_human(@country['extent'], format: '%u', shortUnit: true) %> -
    -
    -

    Total area

    - 851 - Mha -
    -
    -
    -

    LOSS AND GAIN (2001 - 2012)

    -
    -
    -
    -
    Not yet available for this country
    -
    -
    - -
    -
    - - - <% if @country['indepth'].present? %> -
    -

    Global Forest Watch does in-depth work in this country.

    -

    Find more information here

    - -
    - <% end %> - -
    + <%= render 'header' %> + + + <% if @country['indepth'].present? %> +
    +

    Global Forest Watch does in-depth work in this country.

    +

    Find more information here

    +
    + <% end %>
    @@ -467,4 +404,4 @@ <%= render 'shared/sources' %>
    -
    + \ No newline at end of file diff --git a/config/routes.rb b/config/routes.rb index 207194ce9e..a39ef663aa 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -27,6 +27,9 @@ # countries get '/countries' => 'countries#index' get '/country/:id' => 'countries#show', :as => 'country' + # todo => validate id + get '/country/:id/:area_id' => 'countries#show', :as => 'country_area' + get '/countries/overview' => 'countries#overview' # media From 15eb4074aa567c0b3e9fe4955a697fb62fe35d15 Mon Sep 17 00:00:00 2001 From: Pedro Garcia Date: Mon, 2 Jun 2014 12:17:11 +0200 Subject: [PATCH 034/823] hot fixes --- app/assets/javascripts/countries/views/header.js | 4 ++-- app/assets/stylesheets/countries/show.scss | 4 ++++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/app/assets/javascripts/countries/views/header.js b/app/assets/javascripts/countries/views/header.js index 72c11a1a57..fa5074cc79 100644 --- a/app/assets/javascripts/countries/views/header.js +++ b/app/assets/javascripts/countries/views/header.js @@ -31,8 +31,8 @@ gfw.ui.view.CountryHeader = cdb.core.View.extend({ loadArea: function(countryId, areaId) { var area = self.country.get('areas').where({ id_1: Number(areaId) })[0]; self.area = area; - self._setAreaSelector(); if (!self.map) { + self._setAreaSelector(); self._initMap(function() { self._displayArea(area); }); @@ -42,8 +42,8 @@ gfw.ui.view.CountryHeader = cdb.core.View.extend({ }, loadCountry: function(countryId) { - self._setAreaSelector(); if (!self.map) { + self._setAreaSelector(); self._initMap(function() { self._displayCountry(); }); diff --git a/app/assets/stylesheets/countries/show.scss b/app/assets/stylesheets/countries/show.scss index 71d7d90473..3222922bd0 100644 --- a/app/assets/stylesheets/countries/show.scss +++ b/app/assets/stylesheets/countries/show.scss @@ -81,6 +81,10 @@ padding: 10px 15px; width: 100%; + option { + color: #666; + } + &:focus { outline: 0; } From d1ce2e43cdab741842f606a1837d686d43a00615 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A1n=20P=C3=A9rez?= Date: Mon, 2 Jun 2014 16:56:57 +0200 Subject: [PATCH 035/823] umd: different URL layers --- .../gfw/deforestation_tile_layer.js | 20 +++++++++++++++++-- lib/assets/javascripts/gfw/gfw_lib.js.erb | 2 +- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/lib/assets/javascripts/gfw/deforestation_tile_layer.js b/lib/assets/javascripts/gfw/deforestation_tile_layer.js index e7598c725b..388e806014 100644 --- a/lib/assets/javascripts/gfw/deforestation_tile_layer.js +++ b/lib/assets/javascripts/gfw/deforestation_tile_layer.js @@ -185,8 +185,24 @@ var Deforestation = function() { x = Math.pow(2,z) - Math.abs(x); } } - - var url = 'http://earthengine.google.org/static/hansen_2013/gfw_loss_year/' + z + '/' + x + '/' + y + '.png'; + console.log(this.name);debugger; + switch (this.name) { + case 'loss_75' : var url = 'http://earthengine.google.org/static/hansen_2013/gfw_loss_year_75/' + z + '/' + x + '/' + y + '.png'; + break; + case 'loss_50' : var url = 'http://earthengine.google.org/static/hansen_2013/gfw_loss_year_50/' + z + '/' + x + '/' + y + '.png'; + break; + case 'loss_30' : var url = 'http://earthengine.google.org/static/hansen_2013/gfw_loss_year_30/' + z + '/' + x + '/' + y + '.png'; + break; + case 'loss_25' : var url = 'http://earthengine.google.org/static/hansen_2013/gfw_loss_year_25/' + z + '/' + x + '/' + y + '.png'; + break; + case 'loss_20' : var url = 'http://earthengine.google.org/static/hansen_2013/gfw_loss_year_20/' + z + '/' + x + '/' + y + '.png'; + break; + case 'loss_15' : var url = 'http://earthengine.google.org/static/hansen_2013/gfw_loss_year_15/' + z + '/' + x + '/' + y + '.png'; + break; + case 'loss_10' : + case 'loss' : + default: var url = 'http://earthengine.google.org/static/hansen_2013/gfw_loss_year/' + z + '/' + x + '/' + y + '.png'; + } xhr.onload = function () { var url = URL.createObjectURL(this.response), diff --git a/lib/assets/javascripts/gfw/gfw_lib.js.erb b/lib/assets/javascripts/gfw/gfw_lib.js.erb index 3ab5632b31..dadaa68d01 100644 --- a/lib/assets/javascripts/gfw/gfw_lib.js.erb +++ b/lib/assets/javascripts/gfw/gfw_lib.js.erb @@ -478,7 +478,7 @@ GFW.modules.app = function(gfw) { map.overlayMapTypes.insertAt(0, this.umdCoverageLayer); } }, - + _renderForest2000Layer: function() { if (this.forest2000Layer) { this.forest2000Layer.setOpacity(1); From 5f998b6b87af7359ec9d467dcb99053ff59c3e2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A1n=20P=C3=A9rez?= Date: Tue, 3 Jun 2014 09:40:14 +0200 Subject: [PATCH 036/823] umd intensity: change range --- lib/assets/javascripts/gfw/deforestation_tile_layer.js | 2 +- lib/assets/javascripts/gfw/ui/umd_options.js.erb | 8 +++----- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/lib/assets/javascripts/gfw/deforestation_tile_layer.js b/lib/assets/javascripts/gfw/deforestation_tile_layer.js index 388e806014..aad5620567 100644 --- a/lib/assets/javascripts/gfw/deforestation_tile_layer.js +++ b/lib/assets/javascripts/gfw/deforestation_tile_layer.js @@ -185,7 +185,7 @@ var Deforestation = function() { x = Math.pow(2,z) - Math.abs(x); } } - console.log(this.name);debugger; + switch (this.name) { case 'loss_75' : var url = 'http://earthengine.google.org/static/hansen_2013/gfw_loss_year_75/' + z + '/' + x + '/' + y + '.png'; break; diff --git a/lib/assets/javascripts/gfw/ui/umd_options.js.erb b/lib/assets/javascripts/gfw/ui/umd_options.js.erb index 9adbd466de..71833ea710 100644 --- a/lib/assets/javascripts/gfw/ui/umd_options.js.erb +++ b/lib/assets/javascripts/gfw/ui/umd_options.js.erb @@ -189,11 +189,9 @@ gfw.ui.view.umdoptionsDialog = gfw.ui.view.Widget.extend({ _onClickApply: function(e) { e && e.preventDefault(); - this.hide() - }, - - updateMap: function() { - publish('timeline:change_date_fires', [this.model.get('end_day')]); + console.log(this.range);debugger; + this.range = '&begin='+start+'&end='+end; + this.hide(); }, show: function() { From b74e4bd33ecee98b0397e48485241662cc590d46 Mon Sep 17 00:00:00 2001 From: Pedro Garcia Date: Tue, 3 Jun 2014 12:24:22 +0200 Subject: [PATCH 037/823] forest2000 canvas --- app/assets/javascripts/home.js | 1 + .../gfw/deforestation_tile_layer.js | 9 +- .../javascripts/gfw/forest2000_tile_layer.js | 248 ++++++++++++++++++ lib/assets/javascripts/gfw/gfw_lib.js.erb | 132 +++++----- 4 files changed, 324 insertions(+), 66 deletions(-) create mode 100644 lib/assets/javascripts/gfw/forest2000_tile_layer.js diff --git a/app/assets/javascripts/home.js b/app/assets/javascripts/home.js index 1f73e03fa3..50ae1a80c2 100644 --- a/app/assets/javascripts/home.js +++ b/app/assets/javascripts/home.js @@ -12,6 +12,7 @@ //= require markerclusterer_compiled //= require url.min //= require gfw/deforestation_tile_layer +//= require gfw/forest2000_tile_layer //= require gfw/canvas_tile_layer //= require gfw/static_grid_layer_imazon //= require gfw/static_grid_layer diff --git a/lib/assets/javascripts/gfw/deforestation_tile_layer.js b/lib/assets/javascripts/gfw/deforestation_tile_layer.js index 2b53b4220e..ef500f4aac 100644 --- a/lib/assets/javascripts/gfw/deforestation_tile_layer.js +++ b/lib/assets/javascripts/gfw/deforestation_tile_layer.js @@ -215,7 +215,7 @@ var Deforestation = function() { me.init = function(map, callback) { this.map = map; - this.heightLayer = new DeforestationTileLayerThreshold(canvas_setup, filter); + this.heightLayer = new DeforestationTileLayerThreshold(canvas_setup, filter); this.map.overlayMapTypes.insertAt(0, this.heightLayer); if (typeof cover_extent !== 'undefined' && cover_extent.attributes['visible']) callback && callback(); @@ -226,7 +226,12 @@ var Deforestation = function() { me.show = function() { this.heightLayer = new DeforestationTileLayerThreshold(canvas_setup, filter); - this.map.overlayMapTypes.insertAt(1, this.heightLayer); + if (this.map.overlayMapTypes.length < 4) { + this.map.overlayMapTypes.insertAt(1, this.heightLayer); + } else { + console.log(this.map.overlayMapTypes.length); + this.map.overlayMapTypes.insertAt(2, this.heightLayer); + } } me.hide = function() { diff --git a/lib/assets/javascripts/gfw/forest2000_tile_layer.js b/lib/assets/javascripts/gfw/forest2000_tile_layer.js new file mode 100644 index 0000000000..198aace54f --- /dev/null +++ b/lib/assets/javascripts/gfw/forest2000_tile_layer.js @@ -0,0 +1,248 @@ +function Forest2000TileLayer(canvas_setup, filter) { + this.tileSize = new google.maps.Size(256, 256); + this.maxZoom = 19; + this.name = "forest2000"; + this.alt = "Canvas forest2000 tile layer"; + this.tiles = {}; + this.canvas_setup = canvas_setup; + this.filter = filter; +} + +// create a tile with a canvas element +Forest2000TileLayer.prototype.create_tile_canvas = function(coord, zoom, ownerDocument) { + // create canvas and reset style + var canvas = ownerDocument.createElement('canvas'); + canvas.style.border = "none"; + canvas.style.margin = "0"; + canvas.style.padding = "0"; + + // prepare canvas and context sizes + var ctx = canvas.getContext('2d'); + ctx.width = canvas.width = this.tileSize.width; + ctx.height = canvas.height = this.tileSize.height; + + //set unique id + var tile_id = coord.x + '_' + coord.y + '_' + zoom; + canvas.setAttribute('id', tile_id); + if (tile_id in this.tiles) { + delete this.tiles[tile_id]; + } + + this.tiles[tile_id] = canvas; + + // custom setup + if (this.canvas_setup) { + this.canvas_setup(canvas, coord, zoom); + } + + return canvas; +} + +Forest2000TileLayer.prototype.filter_tile = function(canvas) { + var ctx = canvas.getContext('2d'); + ctx.drawImage(canvas.image, 0, 0); + + var I = ctx.getImageData(0, 0, canvas.width, canvas.height); + this.filter.apply(this, [I.data, ctx.width, ctx.height]); + ctx.putImageData(I,0,0); +} + +// render visible tiles on a canvas, return a canvas object +// map: map where tiles are rendering +Forest2000TileLayer.prototype.composed = function(map, w, h) { + var canvas = document.createElement('canvas'); + var ctx = canvas.getContext('2d'); + ctx.width = canvas.width = w || $(map).width(); + ctx.height = canvas.height = h || $(map).height(); + + for(var i in this.tiles) { + var t = this.tiles[i]; + var mpos = $(map).offset(); + var pos = $(t).offset(); + ctx.drawImage(t, pos.left - mpos.left, pos.top - mpos.top); + } + + return canvas; +} + +Forest2000TileLayer.prototype.filter_tiles = function() { + var args = Array.prototype.slice.call(arguments); + for(var c in this.tiles) { + this.filter_tile(this.tiles[c], args); + } +} +Forest2000TileLayer.prototype.getTile = function(coord, zoom, ownerDocument) { + // could be called directly... + return this.create_tile_canvas(coord, zoom, ownerDocument); +}; + +Forest2000TileLayer.prototype.releaseTile = function(tile) { + var id = tile.getAttribute('id'); + delete this.tiles[id]; +}; + +// optimized version for threshold rendering +function Forest2000TileLayerThreshold (canvas_setup, filter) { + Forest2000TileLayer.call(this, canvas_setup, filter); + this.threshold = 0; + this.start_threshold = 0; +} + + +Forest2000TileLayerThreshold.prototype = new Forest2000TileLayer(); + +Forest2000TileLayerThreshold.prototype.filter_tiles = function() { + var new_threshold = arguments[0]; + Forest2000TileLayer.prototype.filter_tiles.apply(this, arguments) + this.threshold = new_threshold; +} + +Forest2000TileLayerThreshold.prototype.filter_tile = function(canvas) { + var ctx = canvas.getContext('2d'); + var coord = canvas.coord; + + if (canvas.coord) { + var zsteps = coord.z - 12; + + if (zsteps > 0) { + ctx['imageSmoothingEnabled'] = false; + ctx['mozImageSmoothingEnabled'] = false; + ctx['webkitImageSmoothingEnabled'] = false; + + var srcX = 256 / Math.pow(2, zsteps) * (coord.x % Math.pow(2, zsteps)); + var srcY = 256 / Math.pow(2, zsteps) * (coord.y % Math.pow(2, zsteps)); + var srcW = 256 / Math.pow(2, zsteps); + var srcH = 256 / Math.pow(2, zsteps); + ctx.drawImage(canvas.image, srcX, srcY, srcW, srcH, 0, 0, 256, 256); + } else { + try { + ctx.drawImage(canvas.image, 0, 0); + } catch(err) { } + } + + var I = ctx.getImageData(0, 0, canvas.width, canvas.height); + this.filter.apply(this, [I.data, ctx.width, ctx.height]); + ctx.putImageData(I,0,0); + } +} + +var Forest2000 = function() { + var me = { }; + + function filter(image_data, w, h, year_start, year_end) { + var components = 4; //rgba + var pixel_pos; + for(var i=0; i < w; ++i) { + for(var j=0; j < h; ++j) { + var pixel_pos = (j*w + i) * components; + //var yearLoss = image_data[pixel_pos]; + var intensity = image_data[pixel_pos + 3]; + var zoom = map.getZoom(); + + + var c = (Math.ceil(3*intensity/255)); + c = 3; + + image_data[pixel_pos] = 0; + image_data[pixel_pos + 1] = intensity*0.7; + image_data[pixel_pos + 2] = 0; + image_data[pixel_pos+ 3]=intensity*0.7 + // if (zoom < 13) { + // image_data[pixel_pos+ 3] = intensity < 10 ? 0: (12/zoom)*255*c/3; + // } else { + // image_data[pixel_pos+ 3] = intensity < 10 ? 0: 255*c/3; + // } + + + } + } + }; + + function canvas_setup(canvas, coord, zoom) { + var xhr = new XMLHttpRequest(); + + var ctx = canvas.getContext('2d'); + + var x = coord.x; + var y = coord.y; + var z = zoom; + + if (zoom > 12) { + x = Math.floor(coord.x/(Math.pow(2, zoom - 12))); + y = Math.floor(coord.y/(Math.pow(2, zoom - 12))); + z = 12; + } else { + y = (y > Math.pow(2,z) ? y % Math.pow(2,z) : y); + if (x >= Math.pow(2,z)) { + x = x % Math.pow(2,z); + } else if (x < 0) { + x = Math.pow(2,z) - Math.abs(x); + } + } + + var url = 'http://earthengine.google.org/static/hansen_2013/tree_alpha/' + z + '/' + x + '/' + y + '.png'; + xhr.onload = function () { + var url = URL.createObjectURL(this.response), + image = new Image(); + + image.onload = function () { + image.crossOrigin = ''; + + canvas.image = image; + canvas.coord = coord; + canvas.coord.z = zoom; + + ctx.drawImage(image, 0, 0); + Forest2000.heightLayer.filter_tile(canvas); + + URL.revokeObjectURL(url); + }; + + image.src = url; + }; + + xhr.open('GET', url, true); + xhr.responseType = 'blob'; + xhr.send(); + } + + me.init = function(map, callback) { + this.map = map; + this.heightLayer = new Forest2000TileLayerThreshold(canvas_setup, filter); + this.map.overlayMapTypes.insertAt(0, this.heightLayer); + + if (typeof cover_extent !== 'undefined' && cover_extent.attributes['visible']) callback && callback(); + + return me; + //this.setup_ui(); + } + + me.show = function() { + this.heightLayer = new Forest2000TileLayerThreshold(canvas_setup, filter); + this.map.overlayMapTypes.insertAt(0, this.heightLayer); + } + + me.hide = function() { + var overlays_length = this.map.overlayMapTypes.getLength(); + + if (overlays_length > 0) { + for (var i = 0; i< overlays_length; i++) { + var layer = this.map.overlayMapTypes.getAt(i); + if (layer && layer.name == 'forest2000') this.map.overlayMapTypes.removeAt(i); + } + } + } + + me.stop = function() { + var overlays_length = this.map.overlayMapTypes.getLength(); + + if (overlays_length > 0) { + for (var i = 0; i< overlays_length; i++) { + var layer = this.map.overlayMapTypes.getAt(i); + if (layer && layer.name == 'forest2000') this.map.overlayMapTypes.removeAt(i); + } + } + } + + return me; +}(); \ No newline at end of file diff --git a/lib/assets/javascripts/gfw/gfw_lib.js.erb b/lib/assets/javascripts/gfw/gfw_lib.js.erb index 77da7987f6..e9d29d81de 100644 --- a/lib/assets/javascripts/gfw/gfw_lib.js.erb +++ b/lib/assets/javascripts/gfw/gfw_lib.js.erb @@ -326,17 +326,18 @@ GFW.modules.app = function(gfw) { _removeForest2000Layer: function() { if (this.forest2000Layer) { - var layers = map.overlayMapTypes.getArray(); - - for (var i = 0; i <= layers.length; i++) { - if (layers[i] == this.forest2000Layer) { - if (this.forest2000Layer) { - this.forest2000Layer.setOpacity(0); - this.forest2000Layer = null; - map.overlayMapTypes.setAt(i, null); - } - } - } + this.forest2000Layer.hide(); + // var layers = map.overlayMapTypes.getArray(); + + // for (var i = 0; i <= layers.length; i++) { + // if (layers[i] == this.forest2000Layer) { + // if (this.forest2000Layer) { + // this.forest2000Layer.setOpacity(0); + // this.forest2000Layer = null; + // map.overlayMapTypes.setAt(i, null); + // } + // } + // } } }, @@ -481,73 +482,76 @@ GFW.modules.app = function(gfw) { _renderForest2000Layer: function() { if (this.forest2000Layer) { - this.forest2000Layer.setOpacity(1); + this.forest2000Layer.show(); + // this.forest2000Layer.setOpacity(1); } else { - function CoordMapType() { } + this.forest2000Layer = Forest2000.init(map); - CoordMapType.prototype.tileSize = new google.maps.Size(256,256); - CoordMapType.prototype.maxZoom = 17; - CoordMapType.prototype.setOpacity = function(opacity) { - $('.tree_alpha').css('opacity', opacity); - }; + // function CoordMapType() { } - CoordMapType.prototype.getTile = function(coord, zoom, ownerDocument) { - var x = coord.x; - var y = coord.y; - var z = zoom; + // CoordMapType.prototype.tileSize = new google.maps.Size(256,256); + // CoordMapType.prototype.maxZoom = 17; + // CoordMapType.prototype.setOpacity = function(opacity) { + // $('.tree_alpha').css('opacity', opacity); + // }; - var zsteps = zoom - 12; + // CoordMapType.prototype.getTile = function(coord, zoom, ownerDocument) { + // var x = coord.x; + // var y = coord.y; + // var z = zoom; - if (zoom > 12) { - x = Math.floor(coord.x/(Math.pow(2, zoom - 12))); - y = Math.floor(coord.y/(Math.pow(2, zoom - 12))); - z = 12; - } else { - y = (y > Math.pow(2,z) ? y % Math.pow(2,z) : y); + // var zsteps = zoom - 12; + + // if (zoom > 12) { + // x = Math.floor(coord.x/(Math.pow(2, zoom - 12))); + // y = Math.floor(coord.y/(Math.pow(2, zoom - 12))); + // z = 12; + // } else { + // y = (y > Math.pow(2,z) ? y % Math.pow(2,z) : y); - if (x >= Math.pow(2,z)) { - x = x % Math.pow(2,z); - } else if (x < 0) { - x = Math.pow(2,z) - Math.abs(x); - } - } + // if (x >= Math.pow(2,z)) { + // x = x % Math.pow(2,z); + // } else if (x < 0) { + // x = Math.pow(2,z) - Math.abs(x); + // } + // } - var url = 'http://earthengine.google.org/static/hansen_2013/tree_alpha/' + z + '/' + x + '/' + y + '.png'; + // var url = 'http://earthengine.google.org/static/hansen_2013/tree_alpha/' + z + '/' + x + '/' + y + '.png'; - var image = new Image(); - image.src = url; - image.className += 'tree_alpha'; + // var image = new Image(); + // image.src = url; + // image.className += 'tree_alpha'; - if (zsteps <= 0) return image; + // if (zsteps <= 0) return image; - image.width = 256 * Math.pow(2, zsteps); - image.height = 256 * Math.pow(2, zsteps); + // image.width = 256 * Math.pow(2, zsteps); + // image.height = 256 * Math.pow(2, zsteps); - if (zsteps > 0) { - var srcX = 256 * (coord.x % Math.pow(2, zsteps)); - var srcY = 256 * (coord.y % Math.pow(2, zsteps)); + // if (zsteps > 0) { + // var srcX = 256 * (coord.x % Math.pow(2, zsteps)); + // var srcY = 256 * (coord.y % Math.pow(2, zsteps)); - image.style.position = 'absolute'; - image.style.top = -srcY + 'px'; - image.style.left = -srcX + 'px'; - } + // image.style.position = 'absolute'; + // image.style.top = -srcY + 'px'; + // image.style.left = -srcX + 'px'; + // } - var div = ownerDocument.createElement('div'); - div.appendChild(image); - div.style.width = this.tileSize.width + 'px'; - div.style.height = this.tileSize.height + 'px'; - div.style.position = 'relative'; - div.style.overflow = 'hidden'; - div.className += 'tree_alpha'; + // var div = ownerDocument.createElement('div'); + // div.appendChild(image); + // div.style.width = this.tileSize.width + 'px'; + // div.style.height = this.tileSize.height + 'px'; + // div.style.position = 'relative'; + // div.style.overflow = 'hidden'; + // div.className += 'tree_alpha'; - return div; - }; + // return div; + // }; - CoordMapType.prototype.name = 'Tile #s'; - CoordMapType.prototype.alt = 'Tile Coordinate Map Type'; + // CoordMapType.prototype.name = 'Tile #s'; + // CoordMapType.prototype.alt = 'Tile Coordinate Map Type'; - this.forest2000Layer = new CoordMapType(); - map.overlayMapTypes.insertAt(0, this.forest2000Layer); + // this.forest2000Layer = new CoordMapType(); + // map.overlayMapTypes.insertAt(0, this.forest2000Layer); } }, @@ -1040,8 +1044,8 @@ GFW.modules.app = function(gfw) { }, _refreshForest2000Layer: function() { - GFW.app._removeForest2000Layer(); - GFW.app._renderForest2000Layer(); + // GFW.app._removeForest2000Layer(); + // GFW.app._renderForest2000Layer(); }, _loadBaseLayer: function() { From caaf830e39f62861238b5960a005e23b37dc7965 Mon Sep 17 00:00:00 2001 From: Pedro Garcia Date: Tue, 3 Jun 2014 12:27:32 +0200 Subject: [PATCH 038/823] hot fixes --- lib/assets/javascripts/gfw/deforestation_tile_layer.js | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/assets/javascripts/gfw/deforestation_tile_layer.js b/lib/assets/javascripts/gfw/deforestation_tile_layer.js index ef500f4aac..7b3f102d3e 100644 --- a/lib/assets/javascripts/gfw/deforestation_tile_layer.js +++ b/lib/assets/javascripts/gfw/deforestation_tile_layer.js @@ -229,7 +229,6 @@ var Deforestation = function() { if (this.map.overlayMapTypes.length < 4) { this.map.overlayMapTypes.insertAt(1, this.heightLayer); } else { - console.log(this.map.overlayMapTypes.length); this.map.overlayMapTypes.insertAt(2, this.heightLayer); } } From 588f2393fcc5386bb3543cbbd1c90c0f163c0141 Mon Sep 17 00:00:00 2001 From: Pedro Garcia Date: Tue, 3 Jun 2014 16:51:51 +0200 Subject: [PATCH 039/823] benji fixes --- .../javascripts/countries/views/header.js | 20 ++++-- app/assets/stylesheets/countries/show.scss | 72 +++++++++---------- app/helpers/countries_helper.rb | 4 +- app/views/countries/_header.html.erb | 6 +- app/views/countries/show.html.erb | 15 ++-- 5 files changed, 64 insertions(+), 53 deletions(-) diff --git a/app/assets/javascripts/countries/views/header.js b/app/assets/javascripts/countries/views/header.js index fa5074cc79..913c2eeaa1 100644 --- a/app/assets/javascripts/countries/views/header.js +++ b/app/assets/javascripts/countries/views/header.js @@ -21,7 +21,9 @@ gfw.ui.view.CountryHeader = cdb.core.View.extend({ var Router = Backbone.Router.extend({ routes: { 'country/:id': 'loadCountry', - 'country/:id/:areaId': 'loadArea' + 'country/:id/': 'loadCountry', + 'country/:id/:areaId': 'loadArea', + 'country/:id/:areaId/': 'loadArea' }, initialize: function() { @@ -80,7 +82,11 @@ gfw.ui.view.CountryHeader = cdb.core.View.extend({ areaName = this.$areaSelector.val(), area = this.country.get('areas').where({ name_1: areaName })[0]; - this.router.navigate('country/' + this.country.get('iso') + '/' + String(area.get('id_1')), {trigger: true}); + if (area) { + this.router.navigate('country/' + this.country.get('iso') + '/' + String(area.get('id_1')), {trigger: true}); + } else { + this._navigateCountry(); + } }, _navigateCountry: function() { @@ -125,7 +131,7 @@ gfw.ui.view.CountryHeader = cdb.core.View.extend({ // Set forest-cover layer this.forestLayer = L.tileLayer('http://earthengine.google.org/static/hansen_2013/tree_alpha/{z}/{x}/{y}.png', { - opacity: 0 + opacity: 0, }).addTo(this.map); // Set country layer @@ -143,19 +149,19 @@ gfw.ui.view.CountryHeader = cdb.core.View.extend({ .done(function(layer) { self.cartodbLayer = layer; - callback(); - self.cartodbLayer.on('loading' , function() { self.$map.removeClass('loaded'); self.forestLayer.setOpacity(0); }); - + self.cartodbLayer.on('load' , function() { setTimeout(function() { self.$map.addClass('loaded'); self.forestLayer.setOpacity(1); - }, 150); + }, 200); }); + + callback(); }); }, diff --git a/app/assets/stylesheets/countries/show.scss b/app/assets/stylesheets/countries/show.scss index 3222922bd0..f3e0f1517f 100644 --- a/app/assets/stylesheets/countries/show.scss +++ b/app/assets/stylesheets/countries/show.scss @@ -54,7 +54,7 @@ h1 { color: white; font-size: 47px; - max-width: 500px; + max-width: 545px; margin-right: 20px; display: inline-block; font-family: $font-regular; @@ -325,48 +325,48 @@ } } // country-details + } // country-header - .country-indepth { - clear: both; - border: 1px solid #CCCCCC; - border-bottom: 0; - height: 185px; - width: 100%; - padding: 35px 20px; - font-family: $font-medium; - @include box-sizing(border-box); - background-image: image-url('backgrounds/country-indepth.png'); - background-repeat: no-repeat; - background-position: 96% 50%; + .country-indepth { + clear: both; + border: 1px solid #CCCCCC; + border-bottom: 0; + height: 185px; + width: 100%; + padding: 35px 20px; + font-family: $font-medium; + @include box-sizing(border-box); + background-image: image-url('backgrounds/country-indepth.png'); + background-repeat: no-repeat; + background-position: 96% 50%; - .country-indepth__title { - font-size: 29px; - } + .country-indepth__title { + font-size: 29px; + } - .country-indepth__body { - text-transform: uppercase; - font-size: 14px; - font-weight: 700; - margin-top: 20px; - } + .country-indepth__body { + text-transform: uppercase; + font-size: 14px; + font-weight: 700; + margin-top: 20px; + } - .country-indepth__links { - margin-top: 25px; - color: #bbb; - font-family: $font-light; - font-size: 13px; + .country-indepth__links { + margin-top: 25px; + color: #bbb; + font-family: $font-light; + font-size: 13px; - a { - font-size: 14px; - display: inline-block; - vertical-align: top; - color: $primary-color; - font-family: $font-medium; - } + a { + font-size: 14px; + display: inline-block; + vertical-align: top; + color: $primary-color; + font-family: $font-medium; } + } - } // country-indepth - } // country-header + } // country-indepth .country-nav-container { width: 100%; diff --git a/app/helpers/countries_helper.rb b/app/helpers/countries_helper.rb index 4f39845cc2..179fddee00 100644 --- a/app/helpers/countries_helper.rb +++ b/app/helpers/countries_helper.rb @@ -12,10 +12,10 @@ def extent_to_human(extent, options = {}) unit = options[:shortUnit] ? "Ha" : '' elsif extent >= 1000 && extent < 1000000 number = "#{number_with_delimiter((extent/1000).round(1))}" - unit = options[:shortUnit] ? "KHa" : 'thousands' + unit = options[:shortUnit] ? "KHa" : 'thousand' else number = "#{number_with_delimiter((extent/1000000).round(1))}" - unit = options[:shortUnit] ? "MHa" : 'millions' + unit = options[:shortUnit] ? "MHa" : 'million' end string = options[:format] diff --git a/app/views/countries/_header.html.erb b/app/views/countries/_header.html.erb index 5e814db619..0da1f3a605 100644 --- a/app/views/countries/_header.html.erb +++ b/app/views/countries/_header.html.erb @@ -24,9 +24,9 @@ <%= extent_to_human(@country['extent'], format: '%u', shortUnit: true) %>
    -

    Total area

    - 851 - Mha +

    Percent Tree Cover

    + 30 + %
    diff --git a/app/views/countries/show.html.erb b/app/views/countries/show.html.erb index 1dc7396a35..56161e526d 100644 --- a/app/views/countries/show.html.erb +++ b/app/views/countries/show.html.erb @@ -15,6 +15,8 @@ <% end %>
    + + + <%= render 'header' %> <% if @country['indepth'].present? %> -
    -

    Global Forest Watch does in-depth work in this country.

    -

    Find more information here

    - +
    +
    +

    Global Forest Watch does in-depth work in this country.

    +

    Find more information here

    + +
    <% end %> From dd150e9af30de0c30c7a111d90544cd3bf8c0cc0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A1n=20P=C3=A9rez?= Date: Tue, 3 Jun 2014 17:08:38 +0200 Subject: [PATCH 040/823] umd layer intensity: canopy on click --- lib/assets/javascripts/gfw/ui/umd_options.js.erb | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/assets/javascripts/gfw/ui/umd_options.js.erb b/lib/assets/javascripts/gfw/ui/umd_options.js.erb index 71833ea710..7d161f1b8c 100644 --- a/lib/assets/javascripts/gfw/ui/umd_options.js.erb +++ b/lib/assets/javascripts/gfw/ui/umd_options.js.erb @@ -189,8 +189,9 @@ gfw.ui.view.umdoptionsDialog = gfw.ui.view.Widget.extend({ _onClickApply: function(e) { e && e.preventDefault(); - console.log(this.range);debugger; - this.range = '&begin='+start+'&end='+end; + config.MAPOPTIONS.analysis = config.MAPOPTIONS.analysis + '&pc=' + this.canopy; + console.log(config.MAPOPTIONS.analysis); + updateHash() this.hide(); }, From 7c401bea8daf4f600938fde425d93fd2e3282ec4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A1n=20P=C3=A9rez?= Date: Tue, 3 Jun 2014 18:28:25 +0200 Subject: [PATCH 041/823] saved changes --- .../gfw/deforestation_tile_layer.js | 23 ++++++++++--------- lib/assets/javascripts/gfw/gfw_lib.js.erb | 19 ++++++++------- .../javascripts/gfw/ui/umd_options.js.erb | 7 +++--- 3 files changed, 25 insertions(+), 24 deletions(-) diff --git a/lib/assets/javascripts/gfw/deforestation_tile_layer.js b/lib/assets/javascripts/gfw/deforestation_tile_layer.js index aad5620567..d1395e316a 100644 --- a/lib/assets/javascripts/gfw/deforestation_tile_layer.js +++ b/lib/assets/javascripts/gfw/deforestation_tile_layer.js @@ -132,7 +132,6 @@ var Deforestation = function() { var me = { }; function filter(image_data, w, h, year_start, year_end) { - //debugger; var components = 4; //rgba var pixel_pos; var zoom = map.getZoom(); @@ -186,20 +185,21 @@ var Deforestation = function() { } } - switch (this.name) { - case 'loss_75' : var url = 'http://earthengine.google.org/static/hansen_2013/gfw_loss_year_75/' + z + '/' + x + '/' + y + '.png'; + switch (this.percent) { + console.log(this.percent) + case 'loss75' : var url = 'http://earthengine.google.org/static/hansen_2013/gfw_loss_year_75/' + z + '/' + x + '/' + y + '.png'; break; - case 'loss_50' : var url = 'http://earthengine.google.org/static/hansen_2013/gfw_loss_year_50/' + z + '/' + x + '/' + y + '.png'; + case 'loss50' : var url = 'http://earthengine.google.org/static/hansen_2013/gfw_loss_year_50/' + z + '/' + x + '/' + y + '.png'; break; - case 'loss_30' : var url = 'http://earthengine.google.org/static/hansen_2013/gfw_loss_year_30/' + z + '/' + x + '/' + y + '.png'; + case 'loss30' : var url = 'http://earthengine.google.org/static/hansen_2013/gfw_loss_year_30/' + z + '/' + x + '/' + y + '.png'; break; - case 'loss_25' : var url = 'http://earthengine.google.org/static/hansen_2013/gfw_loss_year_25/' + z + '/' + x + '/' + y + '.png'; + case 'loss25' : var url = 'http://earthengine.google.org/static/hansen_2013/gfw_loss_year_25/' + z + '/' + x + '/' + y + '.png'; break; - case 'loss_20' : var url = 'http://earthengine.google.org/static/hansen_2013/gfw_loss_year_20/' + z + '/' + x + '/' + y + '.png'; + case 'loss20' : var url = 'http://earthengine.google.org/static/hansen_2013/gfw_loss_year_20/' + z + '/' + x + '/' + y + '.png'; break; - case 'loss_15' : var url = 'http://earthengine.google.org/static/hansen_2013/gfw_loss_year_15/' + z + '/' + x + '/' + y + '.png'; + case 'loss_5' : var url = 'http://earthengine.google.org/static/hansen_2013/gfw_loss_year_15/' + z + '/' + x + '/' + y + '.png'; break; - case 'loss_10' : + case 'loss10' : case 'loss' : default: var url = 'http://earthengine.google.org/static/hansen_2013/gfw_loss_year/' + z + '/' + x + '/' + y + '.png'; } @@ -229,11 +229,12 @@ var Deforestation = function() { xhr.send(); } - me.init = function(map, callback) { + me.init = function(map, percent, callback) { + debugger; this.map = map; this.heightLayer = new DeforestationTileLayerThreshold(canvas_setup, filter); this.map.overlayMapTypes.insertAt(0, this.heightLayer); - + this.percent = percent; if (typeof cover_extent !== 'undefined' && cover_extent.attributes['visible']) callback && callback(); return me; diff --git a/lib/assets/javascripts/gfw/gfw_lib.js.erb b/lib/assets/javascripts/gfw/gfw_lib.js.erb index dadaa68d01..0eb172f223 100644 --- a/lib/assets/javascripts/gfw/gfw_lib.js.erb +++ b/lib/assets/javascripts/gfw/gfw_lib.js.erb @@ -757,7 +757,6 @@ GFW.modules.app = function(gfw) { updateBaseLayer: function(baselayer) { this.currentBaseLayer = config.BASELAYER = baselayer; - this._toggleTimeLayer(); this._loadBaseLayer(); @@ -983,8 +982,8 @@ GFW.modules.app = function(gfw) { map.getDiv().appendChild(gee_link) } }, 2000); - - this.time_layer_loss = Deforestation.init(map, function() { +debugger; + this.time_layer_loss = Deforestation.init(map, this.currentBaseLayer, function() { GFW.app._removeForest2000Layer(); GFW.app._renderForest2000Layer(); }); @@ -1047,14 +1046,14 @@ GFW.modules.app = function(gfw) { _loadBaseLayer: function() { var table_name = null; - if (this.currentBaseLayer === 'loss') { - if (config.mapLoaded && !this.time_layer_loss) { + if (this.currentBaseLayer.indexOf('loss') === 0) { + // if (config.mapLoaded && !this.time_layer_loss) { this._loadTimeLayerLoss(); - } else { - if (this.time_layer_loss) { - this.time_layer_loss.show(); - } - } + // } else { + // if (this.time_layer_loss) { + // this.time_layer_loss.show(); + // } + // } return; diff --git a/lib/assets/javascripts/gfw/ui/umd_options.js.erb b/lib/assets/javascripts/gfw/ui/umd_options.js.erb index 7d161f1b8c..4f019840e8 100644 --- a/lib/assets/javascripts/gfw/ui/umd_options.js.erb +++ b/lib/assets/javascripts/gfw/ui/umd_options.js.erb @@ -189,9 +189,10 @@ gfw.ui.view.umdoptionsDialog = gfw.ui.view.Widget.extend({ _onClickApply: function(e) { e && e.preventDefault(); - config.MAPOPTIONS.analysis = config.MAPOPTIONS.analysis + '&pc=' + this.canopy; - console.log(config.MAPOPTIONS.analysis); - updateHash() + + config.BASELAYER = 'loss' + this.canopy; + updateHash(); + GFW.app.updateBaseLayer(config.BASELAYER) this.hide(); }, From 937bb09064d67bb8af431104e7a2520a87fe5aa3 Mon Sep 17 00:00:00 2001 From: Pedro Garcia Date: Tue, 3 Jun 2014 18:48:25 +0200 Subject: [PATCH 042/823] canvas country map --- .../javascripts/countries/views/header.js | 109 ++++++++++++++++-- 1 file changed, 102 insertions(+), 7 deletions(-) diff --git a/app/assets/javascripts/countries/views/header.js b/app/assets/javascripts/countries/views/header.js index 913c2eeaa1..1d0c4c0943 100644 --- a/app/assets/javascripts/countries/views/header.js +++ b/app/assets/javascripts/countries/views/header.js @@ -56,7 +56,6 @@ gfw.ui.view.CountryHeader = cdb.core.View.extend({ }); - this.country.fetch({ success: function() { self.router = new Router(); @@ -107,6 +106,56 @@ gfw.ui.view.CountryHeader = cdb.core.View.extend({ }); }, + _filterCanvasImage: function(imageData, w, h) { + var components = 4, + pixelPos; + + for(var i = 0 ; i < w; ++i) { + for(var j = 0; j < h; ++j) { + var pixelPos = (j * w + i) * components, + intensity = imageData[pixelPos + 3]; + + imageData[pixelPos] = 0; + imageData[pixelPos + 1] = intensity * 0.7; + imageData[pixelPos + 2] = 250; + imageData[pixelPos+ 3] = intensity * 0.7; + } + } + }, + + _drawImageCanvas: function(canvas) { + var ctx = canvas.getContext('2d'), + coord = canvas.coord; + + // en este momento el canvas puede tener una imagen que haya que agrandar, ya que pertenece a un zoom menor: + if (canvas.coord) { + var zsteps = coord.z - 12; + + // si hay que agrandar canvas: + if (zsteps > 0) { + ctx['imageSmoothingEnabled'] = false; + ctx['mozImageSmoothingEnabled'] = false; + ctx['webkitImageSmoothingEnabled'] = false; + + var srcX = 256 / Math.pow(2, zsteps) * (coord.x % Math.pow(2, zsteps)), + srcY = 256 / Math.pow(2, zsteps) * (coord.y % Math.pow(2, zsteps)), + srcW = 256 / Math.pow(2, zsteps), + srcH = 256 / Math.pow(2, zsteps); + ctx.drawImage(canvas.image, srcX, srcY, srcW, srcH, 0, 0, 256, 256); + + // si no hay que agrandarlo: + } else { + try { + ctx.drawImage(canvas.image, 0, 0); + } catch(err) { } + } + + var I = ctx.getImageData(0, 0, canvas.width, canvas.height); + this._filterCanvasImage(I.data, canvas.width, canvas.height); + ctx.putImageData(I, 0, 0); + } + }, + _renderMap: function(callback) { var self = this; @@ -129,10 +178,56 @@ gfw.ui.view.CountryHeader = cdb.core.View.extend({ fadeAnimation: false, }); - // Set forest-cover layer - this.forestLayer = L.tileLayer('http://earthengine.google.org/static/hansen_2013/tree_alpha/{z}/{x}/{y}.png', { - opacity: 0, - }).addTo(this.map); + this.forestLayer = L.tileLayer.canvas().addTo(this.map); + + this.forestLayer.drawTile = function(canvas, tilePoint, zoom) { + var xhr = new XMLHttpRequest(), + ctx = canvas.getContext('2d'); + + var x = tilePoint.x, + y = tilePoint.y, + z = zoom; + + // floor = round a number downward to its nearest integer + // pow = return the value of the number 4 to be the power of 3 (4*4*4): + if (zoom > 12) { + x = Math.floor(x / (Math.pow(2, zoom - 12))); + y = Math.floor(y / (Math.pow(2, zoom - 12))); + z = 12; + } else { + y = (y > Math.pow(2, z) ? y % Math.pow(2,z) : y); + if (x >= Math.pow(2, z)) { + x = x % Math.pow(2, z); + } else if (x < 0) { + x = Math.pow(2,z) - Math.abs(x); + } + } + + var url = 'http://earthengine.google.org/static/hansen_2013/tree_alpha/' + z + '/' + x + '/' + y + '.png'; + + xhr.onload = function () { + var url = URL.createObjectURL(this.response), + image = new Image(); + + image.onload = function () { + image.crossOrigin = ''; + + canvas.image = image; + canvas.coord = {x: tilePoint.x, y: tilePoint.y}; + canvas.coord.z = zoom; + + self._drawImageCanvas(canvas); + + URL.revokeObjectURL(url); + }; + + image.src = url; + }; + + xhr.open('GET', url, true); + xhr.responseType = 'blob'; + xhr.send(); + } // Set country layer cartodb.createLayer(this.map, { @@ -151,13 +246,13 @@ gfw.ui.view.CountryHeader = cdb.core.View.extend({ self.cartodbLayer.on('loading' , function() { self.$map.removeClass('loaded'); - self.forestLayer.setOpacity(0); + // self.forestLayer.setOpacity(0); }); self.cartodbLayer.on('load' , function() { setTimeout(function() { self.$map.addClass('loaded'); - self.forestLayer.setOpacity(1); + // self.forestLayer.setOpacity(1); }, 200); }); From df4e23d5302cc0d1a997c288b1bc4683d355c5b4 Mon Sep 17 00:00:00 2001 From: Pedro Garcia Date: Tue, 3 Jun 2014 18:50:29 +0200 Subject: [PATCH 043/823] comments removed --- app/assets/javascripts/countries/views/header.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/app/assets/javascripts/countries/views/header.js b/app/assets/javascripts/countries/views/header.js index 1d0c4c0943..d92536cbf1 100644 --- a/app/assets/javascripts/countries/views/header.js +++ b/app/assets/javascripts/countries/views/header.js @@ -127,11 +127,9 @@ gfw.ui.view.CountryHeader = cdb.core.View.extend({ var ctx = canvas.getContext('2d'), coord = canvas.coord; - // en este momento el canvas puede tener una imagen que haya que agrandar, ya que pertenece a un zoom menor: if (canvas.coord) { var zsteps = coord.z - 12; - // si hay que agrandar canvas: if (zsteps > 0) { ctx['imageSmoothingEnabled'] = false; ctx['mozImageSmoothingEnabled'] = false; @@ -143,7 +141,6 @@ gfw.ui.view.CountryHeader = cdb.core.View.extend({ srcH = 256 / Math.pow(2, zsteps); ctx.drawImage(canvas.image, srcX, srcY, srcW, srcH, 0, 0, 256, 256); - // si no hay que agrandarlo: } else { try { ctx.drawImage(canvas.image, 0, 0); From 470ec570743a37034ee5f401f325edf8ed82e7f5 Mon Sep 17 00:00:00 2001 From: Pedro Garcia Date: Wed, 4 Jun 2014 12:58:11 +0200 Subject: [PATCH 044/823] canvas layer model --- .../javascripts/countries/views/header.js | 265 +++++++++++------- 1 file changed, 161 insertions(+), 104 deletions(-) diff --git a/app/assets/javascripts/countries/views/header.js b/app/assets/javascripts/countries/views/header.js index d92536cbf1..893fdb7910 100644 --- a/app/assets/javascripts/countries/views/header.js +++ b/app/assets/javascripts/countries/views/header.js @@ -1,5 +1,160 @@ gfw.ui.model.CountryHeaderStatus = cdb.core.Model.extend({}); +gfw.ui.model.layersOptions = Backbone.Model.extend({ + + initialize: function(options) { + options = options || {}; + + var layers = { + 'forest2000': { + url: 'http://earthengine.google.org/static/hansen_2013/tree_alpha/%z/%x/%y.png', + dataMaxZoom: 12, + tileSize: [256, 256], + _filterCanvasImage: function(imageData, w, h) { + var components = 4, + pixelPos; + + for(var i = 0 ; i < w; ++i) { + for(var j = 0; j < h; ++j) { + var pixelPos = (j * w + i) * components, + intensity = imageData[pixelPos + 3]; + + imageData[pixelPos] = 0; + imageData[pixelPos + 1] = intensity * 0.7; + imageData[pixelPos + 2] = 0; + imageData[pixelPos+ 3] = intensity * 0.7; + } + } + } + } + }; + + var self = this, + layer = layers[options.layer]; + + if (layer) { + _.each(layer, function(row, i) { + self.set(i, row); + }) + } + }, + + +}); + +gfw.ui.view.leafletCanvasLayer = Backbone.View.extend({ + + initialize: function(options) { + var self = this; + options = options || {layerName: ''}; + + this.layerOptions = new gfw.ui.model.layersOptions({layer: options.layerName}); + _.extend(this, this.layerOptions.toJSON()); + + this.layer = L.tileLayer.canvas(); + + this.layer.drawTile = function(canvas, tilePoint, zoom) { + var xhr = new XMLHttpRequest(), + ctx = canvas.getContext('2d'); + + var x = tilePoint.x, + y = tilePoint.y, + z = zoom, + mz = self.dataMaxZoom; + + if (zoom > mz) { + x = Math.floor(x / (Math.pow(2, zoom - mz))); + y = Math.floor(y / (Math.pow(2, zoom - mz))); + z = mz; + } else { + y = (y > Math.pow(2, z) ? y % Math.pow(2,z) : y); + if (x >= Math.pow(2, z)) { + x = x % Math.pow(2, z); + } else if (x < 0) { + x = Math.pow(2,z) - Math.abs(x); + } + } + + var url = self.url.replace('%z', z).replace('%x', x).replace('%y', y); + + xhr.onload = function () { + var url = URL.createObjectURL(this.response), + image = new Image(); + + image.onload = function () { + image.crossOrigin = ''; + + canvas.image = image; + canvas.coord = {x: tilePoint.x, y: tilePoint.y}; + canvas.coord.z = zoom; + + self._drawImageCanvas(canvas); + + URL.revokeObjectURL(url); + }; + + image.src = url; + }; + + xhr.open('GET', url, true); + xhr.responseType = 'blob'; + xhr.send(); + } + }, + + _filterCanvasImage: function(imageData, w, h) { + var components = 4, + pixelPos; + + for(var i = 0 ; i < w; ++i) { + for(var j = 0; j < h; ++j) { + var pixelPos = (j * w + i) * components, + intensity = imageData[pixelPos + 3]; + + imageData[pixelPos] = 0; + imageData[pixelPos + 1] = intensity * 0.7; + imageData[pixelPos + 2] = 255; + imageData[pixelPos+ 3] = intensity * 0.7; + } + } + }, + + _drawImageCanvas: function(canvas) { + var ctx = canvas.getContext('2d'), + coord = canvas.coord; + + if (canvas.coord) { + var zsteps = coord.z - this.dataMaxZoom; + + if (zsteps > 0) { + ctx['imageSmoothingEnabled'] = false; + ctx['mozImageSmoothingEnabled'] = false; + ctx['webkitImageSmoothingEnabled'] = false; + + var srcX = 256 / Math.pow(2, zsteps) * (coord.x % Math.pow(2, zsteps)), + srcY = 256 / Math.pow(2, zsteps) * (coord.y % Math.pow(2, zsteps)), + srcW = 256 / Math.pow(2, zsteps), + srcH = 256 / Math.pow(2, zsteps); + ctx.drawImage(canvas.image, srcX, srcY, srcW, srcH, 0, 0, 256, 256); + + } else { + try { + ctx.drawImage(canvas.image, 0, 0); + } catch(err) { } + } + + var I = ctx.getImageData(0, 0, canvas.width, canvas.height); + this._filterCanvasImage(I.data, canvas.width, canvas.height); + ctx.putImageData(I, 0, 0); + } + }, + + getLayer: function() { + return this.layer; + } + +}); + gfw.ui.view.CountryHeader = cdb.core.View.extend({ el: $('.country-header'), @@ -106,53 +261,6 @@ gfw.ui.view.CountryHeader = cdb.core.View.extend({ }); }, - _filterCanvasImage: function(imageData, w, h) { - var components = 4, - pixelPos; - - for(var i = 0 ; i < w; ++i) { - for(var j = 0; j < h; ++j) { - var pixelPos = (j * w + i) * components, - intensity = imageData[pixelPos + 3]; - - imageData[pixelPos] = 0; - imageData[pixelPos + 1] = intensity * 0.7; - imageData[pixelPos + 2] = 250; - imageData[pixelPos+ 3] = intensity * 0.7; - } - } - }, - - _drawImageCanvas: function(canvas) { - var ctx = canvas.getContext('2d'), - coord = canvas.coord; - - if (canvas.coord) { - var zsteps = coord.z - 12; - - if (zsteps > 0) { - ctx['imageSmoothingEnabled'] = false; - ctx['mozImageSmoothingEnabled'] = false; - ctx['webkitImageSmoothingEnabled'] = false; - - var srcX = 256 / Math.pow(2, zsteps) * (coord.x % Math.pow(2, zsteps)), - srcY = 256 / Math.pow(2, zsteps) * (coord.y % Math.pow(2, zsteps)), - srcW = 256 / Math.pow(2, zsteps), - srcH = 256 / Math.pow(2, zsteps); - ctx.drawImage(canvas.image, srcX, srcY, srcW, srcH, 0, 0, 256, 256); - - } else { - try { - ctx.drawImage(canvas.image, 0, 0); - } catch(err) { } - } - - var I = ctx.getImageData(0, 0, canvas.width, canvas.height); - this._filterCanvasImage(I.data, canvas.width, canvas.height); - ctx.putImageData(I, 0, 0); - } - }, - _renderMap: function(callback) { var self = this; @@ -163,7 +271,6 @@ gfw.ui.view.CountryHeader = cdb.core.View.extend({ this.map = new L.Map('countryMap', { center: [0, 0], - maxBounds: this.country.get('bounds'), zoom: 3, zoomControl: false, dragging: false, @@ -175,56 +282,9 @@ gfw.ui.view.CountryHeader = cdb.core.View.extend({ fadeAnimation: false, }); - this.forestLayer = L.tileLayer.canvas().addTo(this.map); - - this.forestLayer.drawTile = function(canvas, tilePoint, zoom) { - var xhr = new XMLHttpRequest(), - ctx = canvas.getContext('2d'); - - var x = tilePoint.x, - y = tilePoint.y, - z = zoom; - - // floor = round a number downward to its nearest integer - // pow = return the value of the number 4 to be the power of 3 (4*4*4): - if (zoom > 12) { - x = Math.floor(x / (Math.pow(2, zoom - 12))); - y = Math.floor(y / (Math.pow(2, zoom - 12))); - z = 12; - } else { - y = (y > Math.pow(2, z) ? y % Math.pow(2,z) : y); - if (x >= Math.pow(2, z)) { - x = x % Math.pow(2, z); - } else if (x < 0) { - x = Math.pow(2,z) - Math.abs(x); - } - } - - var url = 'http://earthengine.google.org/static/hansen_2013/tree_alpha/' + z + '/' + x + '/' + y + '.png'; - - xhr.onload = function () { - var url = URL.createObjectURL(this.response), - image = new Image(); - - image.onload = function () { - image.crossOrigin = ''; - - canvas.image = image; - canvas.coord = {x: tilePoint.x, y: tilePoint.y}; - canvas.coord.z = zoom; - - self._drawImageCanvas(canvas); - - URL.revokeObjectURL(url); - }; - - image.src = url; - }; - - xhr.open('GET', url, true); - xhr.responseType = 'blob'; - xhr.send(); - } + this.forestLayer = new gfw.ui.view.leafletCanvasLayer({ + layerName: 'forest2000' + }).getLayer().addTo(this.map); // Set country layer cartodb.createLayer(this.map, { @@ -240,17 +300,14 @@ gfw.ui.view.CountryHeader = cdb.core.View.extend({ .addTo(this.map) .done(function(layer) { self.cartodbLayer = layer; - self.cartodbLayer.on('loading' , function() { self.$map.removeClass('loaded'); - // self.forestLayer.setOpacity(0); + self.forestLayer.setOpacity(0); }); self.cartodbLayer.on('load' , function() { - setTimeout(function() { - self.$map.addClass('loaded'); - // self.forestLayer.setOpacity(1); - }, 200); + self.$map.addClass('loaded'); + self.forestLayer.setOpacity(1); }); callback(); From d7f8b6112a90c5ec4e7faae553fa68f8b13b3328 Mon Sep 17 00:00:00 2001 From: Pedro Garcia Date: Wed, 4 Jun 2014 13:22:15 +0200 Subject: [PATCH 045/823] hot fixes --- app/assets/javascripts/countries/views/header.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/app/assets/javascripts/countries/views/header.js b/app/assets/javascripts/countries/views/header.js index 893fdb7910..8f78ff26de 100644 --- a/app/assets/javascripts/countries/views/header.js +++ b/app/assets/javascripts/countries/views/header.js @@ -310,6 +310,12 @@ gfw.ui.view.CountryHeader = cdb.core.View.extend({ self.forestLayer.setOpacity(1); }); + self.cartodbLayer.on('tileload' , function() { + // _.each(self.map._layers, function(layer) { + // console.log(layer._tilesToLoad); + // }); + }); + callback(); }); }, From fcf1c9e95c0e38e6f8aba5cf3dcdbf052c22b3cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A1n=20P=C3=A9rez?= Date: Wed, 4 Jun 2014 15:40:26 +0200 Subject: [PATCH 046/823] changes current fix layer --- app/assets/stylesheets/home.scss | 2 +- app/views/shared/_js_templates.html.erb | 2 +- .../javascripts/gfw/deforestation_tile_layer.js | 9 +++++---- lib/assets/javascripts/gfw/gfw_lib.js.erb | 17 +++++++++-------- .../javascripts/gfw/ui/umd_options.js.erb | 2 +- 5 files changed, 17 insertions(+), 15 deletions(-) diff --git a/app/assets/stylesheets/home.scss b/app/assets/stylesheets/home.scss index 9b7692f586..bfa603018c 100644 --- a/app/assets/stylesheets/home.scss +++ b/app/assets/stylesheets/home.scss @@ -1642,7 +1642,7 @@ body { height: 6px; box-shadow: 1px 0 3px #cccccc; right: 0; - width: 60px; + width: 350px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; pointer-events:none; diff --git a/app/views/shared/_js_templates.html.erb b/app/views/shared/_js_templates.html.erb index 4242d2f675..3f34bf7025 100644 --- a/app/views/shared/_js_templates.html.erb +++ b/app/views/shared/_js_templates.html.erb @@ -651,7 +651,7 @@
  • 100%
  • - +

    diff --git a/lib/assets/javascripts/gfw/deforestation_tile_layer.js b/lib/assets/javascripts/gfw/deforestation_tile_layer.js index d1395e316a..ce3b645d61 100644 --- a/lib/assets/javascripts/gfw/deforestation_tile_layer.js +++ b/lib/assets/javascripts/gfw/deforestation_tile_layer.js @@ -185,8 +185,9 @@ var Deforestation = function() { } } - switch (this.percent) { - console.log(this.percent) + percent = (!! this.percent === false) ? 10 : this.percent; + console.log(percent, this.name) + switch (percent) { case 'loss75' : var url = 'http://earthengine.google.org/static/hansen_2013/gfw_loss_year_75/' + z + '/' + x + '/' + y + '.png'; break; case 'loss50' : var url = 'http://earthengine.google.org/static/hansen_2013/gfw_loss_year_50/' + z + '/' + x + '/' + y + '.png'; @@ -197,7 +198,7 @@ var Deforestation = function() { break; case 'loss20' : var url = 'http://earthengine.google.org/static/hansen_2013/gfw_loss_year_20/' + z + '/' + x + '/' + y + '.png'; break; - case 'loss_5' : var url = 'http://earthengine.google.org/static/hansen_2013/gfw_loss_year_15/' + z + '/' + x + '/' + y + '.png'; + case 'loss15' : var url = 'http://earthengine.google.org/static/hansen_2013/gfw_loss_year_15/' + z + '/' + x + '/' + y + '.png'; break; case 'loss10' : case 'loss' : @@ -230,11 +231,11 @@ var Deforestation = function() { } me.init = function(map, percent, callback) { - debugger; this.map = map; this.heightLayer = new DeforestationTileLayerThreshold(canvas_setup, filter); this.map.overlayMapTypes.insertAt(0, this.heightLayer); this.percent = percent; +debugger; if (typeof cover_extent !== 'undefined' && cover_extent.attributes['visible']) callback && callback(); return me; diff --git a/lib/assets/javascripts/gfw/gfw_lib.js.erb b/lib/assets/javascripts/gfw/gfw_lib.js.erb index 0eb172f223..488aae4661 100644 --- a/lib/assets/javascripts/gfw/gfw_lib.js.erb +++ b/lib/assets/javascripts/gfw/gfw_lib.js.erb @@ -756,6 +756,7 @@ GFW.modules.app = function(gfw) { }, updateBaseLayer: function(baselayer) { + debugger; this.currentBaseLayer = config.BASELAYER = baselayer; this._toggleTimeLayer(); this._loadBaseLayer(); @@ -982,7 +983,7 @@ GFW.modules.app = function(gfw) { map.getDiv().appendChild(gee_link) } }, 2000); -debugger; + this.time_layer_loss = Deforestation.init(map, this.currentBaseLayer, function() { GFW.app._removeForest2000Layer(); GFW.app._renderForest2000Layer(); @@ -1045,15 +1046,15 @@ debugger; _loadBaseLayer: function() { var table_name = null; - +debugger; if (this.currentBaseLayer.indexOf('loss') === 0) { - // if (config.mapLoaded && !this.time_layer_loss) { + if (config.mapLoaded && !this.time_layer_loss) { this._loadTimeLayerLoss(); - // } else { - // if (this.time_layer_loss) { - // this.time_layer_loss.show(); - // } - // } + } else { + if (this.time_layer_loss) { + this.time_layer_loss.show(); + } + } return; diff --git a/lib/assets/javascripts/gfw/ui/umd_options.js.erb b/lib/assets/javascripts/gfw/ui/umd_options.js.erb index 4f019840e8..a6cfcc5131 100644 --- a/lib/assets/javascripts/gfw/ui/umd_options.js.erb +++ b/lib/assets/javascripts/gfw/ui/umd_options.js.erb @@ -189,7 +189,7 @@ gfw.ui.view.umdoptionsDialog = gfw.ui.view.Widget.extend({ _onClickApply: function(e) { e && e.preventDefault(); - +console.log(config.BASELAYER) config.BASELAYER = 'loss' + this.canopy; updateHash(); GFW.app.updateBaseLayer(config.BASELAYER) From 58ceefb195800e4337f852ac8160c90fd8999653 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A1n=20P=C3=A9rez?= Date: Thu, 5 Jun 2014 13:26:23 +0200 Subject: [PATCH 047/823] umd intensity: ended --- app/controllers/home_controller.rb | 2 +- lib/assets/javascripts/gfw/deforestation_tile_layer.js | 7 ++----- lib/assets/javascripts/gfw/gfw_lib.js.erb | 3 +-- lib/assets/javascripts/gfw/map.js | 7 +++++++ lib/assets/javascripts/gfw/ui/analysis.js.erb | 6 +++--- lib/assets/javascripts/gfw/ui/timeline.js | 5 ++--- lib/assets/javascripts/gfw/ui/umd_options.js.erb | 1 - 7 files changed, 16 insertions(+), 15 deletions(-) diff --git a/app/controllers/home_controller.rb b/app/controllers/home_controller.rb index c6c9660381..11af019b05 100644 --- a/app/controllers/home_controller.rb +++ b/app/controllers/home_controller.rb @@ -18,7 +18,7 @@ def accept_and_redirect def validate_url if (params[:basemap].present? && params[:baselayer].present?) - baselayers = ['loss', 'forma', 'imazon', 'modis', 'fires', 'none'] + baselayers = ['loss','loss10','loss15','loss20','loss20','loss25','loss30','loss50','loss75','forma', 'imazon', 'modis', 'fires', 'none'] basemaps = ['grayscale', 'terrain', 'satellite', 'roads', 'treeheight'] for i in 1999..2012 diff --git a/lib/assets/javascripts/gfw/deforestation_tile_layer.js b/lib/assets/javascripts/gfw/deforestation_tile_layer.js index ce3b645d61..c159ca5e1d 100644 --- a/lib/assets/javascripts/gfw/deforestation_tile_layer.js +++ b/lib/assets/javascripts/gfw/deforestation_tile_layer.js @@ -184,10 +184,8 @@ var Deforestation = function() { x = Math.pow(2,z) - Math.abs(x); } } - - percent = (!! this.percent === false) ? 10 : this.percent; - console.log(percent, this.name) - switch (percent) { + config.BASELAYER = 'loss'; //hardcoded to make it work until we have other data layers + switch (config.BASELAYER) { case 'loss75' : var url = 'http://earthengine.google.org/static/hansen_2013/gfw_loss_year_75/' + z + '/' + x + '/' + y + '.png'; break; case 'loss50' : var url = 'http://earthengine.google.org/static/hansen_2013/gfw_loss_year_50/' + z + '/' + x + '/' + y + '.png'; @@ -235,7 +233,6 @@ var Deforestation = function() { this.heightLayer = new DeforestationTileLayerThreshold(canvas_setup, filter); this.map.overlayMapTypes.insertAt(0, this.heightLayer); this.percent = percent; -debugger; if (typeof cover_extent !== 'undefined' && cover_extent.attributes['visible']) callback && callback(); return me; diff --git a/lib/assets/javascripts/gfw/gfw_lib.js.erb b/lib/assets/javascripts/gfw/gfw_lib.js.erb index 488aae4661..c67755d323 100644 --- a/lib/assets/javascripts/gfw/gfw_lib.js.erb +++ b/lib/assets/javascripts/gfw/gfw_lib.js.erb @@ -756,7 +756,6 @@ GFW.modules.app = function(gfw) { }, updateBaseLayer: function(baselayer) { - debugger; this.currentBaseLayer = config.BASELAYER = baselayer; this._toggleTimeLayer(); this._loadBaseLayer(); @@ -1046,7 +1045,7 @@ GFW.modules.app = function(gfw) { _loadBaseLayer: function() { var table_name = null; -debugger; + debugger; if (this.currentBaseLayer.indexOf('loss') === 0) { if (config.mapLoaded && !this.time_layer_loss) { this._loadTimeLayerLoss(); diff --git a/lib/assets/javascripts/gfw/map.js b/lib/assets/javascripts/gfw/map.js index 07dce7eb05..c84e059821 100644 --- a/lib/assets/javascripts/gfw/map.js +++ b/lib/assets/javascripts/gfw/map.js @@ -57,6 +57,13 @@ $(function() { var baselayers = [ 'loss', + 'loss10', + 'loss15', + 'loss20', + 'loss25', + 'loss30', + 'loss50', + 'loss75', 'forma', 'imazon', 'modis', diff --git a/lib/assets/javascripts/gfw/ui/analysis.js.erb b/lib/assets/javascripts/gfw/ui/analysis.js.erb index 9736bb8134..e7305d0cc0 100644 --- a/lib/assets/javascripts/gfw/ui/analysis.js.erb +++ b/lib/assets/javascripts/gfw/ui/analysis.js.erb @@ -369,7 +369,7 @@ gfw.ui.view.Analysis = cdb.core.View.extend({ this.info.setDraggable(true); - var dataset = (baselayer === 'loss') ? 'umd' : baselayer; + var dataset = (baselayer.indexOf('loss') === 0 ) ? 'umd' : baselayer; if (!this.range && url('?begin') && url('?end')) { this.range = '&begin=' + url('?begin') + '&end=' + url('?end'); @@ -402,7 +402,7 @@ gfw.ui.view.Analysis = cdb.core.View.extend({ iend = '2013-0-10-01' || url('?end'); range = this.range || '&begin=' + ibegin + '&end=' + iend; - } if (baselayer === 'loss') { + } if (baselayer.indexOf('loss') === 0) { var lbegin = '2001' || url('?begin'), lend = '2013' || url('?end'); range = this.range || '&begin=' + lbegin +'&end=' + lend; @@ -438,7 +438,7 @@ gfw.ui.view.Analysis = cdb.core.View.extend({ ga('send', 'event', 'Analysis', 'Success', iso + ' - ' + baselayer); - if (baselayer === 'loss' && data) { + if (baselayer.indexOf('loss') === 0 && data) { var loss = formatNumber(Math.ceil((data.loss) * 10) / 10, true); var gain = formatNumber(Math.ceil((data.gain) * 10) / 10, true); diff --git a/lib/assets/javascripts/gfw/ui/timeline.js b/lib/assets/javascripts/gfw/ui/timeline.js index 4dd6a3f8f3..9593ee190b 100644 --- a/lib/assets/javascripts/gfw/ui/timeline.js +++ b/lib/assets/javascripts/gfw/ui/timeline.js @@ -83,13 +83,12 @@ gfw.ui.view.Timeline = cdb.core.View.extend({ var that = this; var baselayer = this.model.get('baselayer'); - if(this.model.get('hidden')) { $(this.$el).fadeOut(); } else { $(this.$el).fadeIn(200, function() { if (!that.initialized) { - if (baselayer === 'loss') { + if (baselayer.indexOf('loss') === 0) { that.loss_timeline.show(); } else if (baselayer === 'forma') { that.forma_timeline.show(); @@ -112,7 +111,7 @@ gfw.ui.view.Timeline = cdb.core.View.extend({ var baselayer = this.model.get('baselayer'); - if (baselayer === 'loss') { + if (baselayer.indexOf('loss') === 0) { that.loss_timeline.show(); } else { that.loss_timeline.hide( (baselayer === null ) ) diff --git a/lib/assets/javascripts/gfw/ui/umd_options.js.erb b/lib/assets/javascripts/gfw/ui/umd_options.js.erb index a6cfcc5131..3b4748cdd7 100644 --- a/lib/assets/javascripts/gfw/ui/umd_options.js.erb +++ b/lib/assets/javascripts/gfw/ui/umd_options.js.erb @@ -189,7 +189,6 @@ gfw.ui.view.umdoptionsDialog = gfw.ui.view.Widget.extend({ _onClickApply: function(e) { e && e.preventDefault(); -console.log(config.BASELAYER) config.BASELAYER = 'loss' + this.canopy; updateHash(); GFW.app.updateBaseLayer(config.BASELAYER) From 384c3de7a9f7f0e136ed1bdd17c5eea25718fb51 Mon Sep 17 00:00:00 2001 From: Pedro Garcia Date: Thu, 5 Jun 2014 15:27:38 +0200 Subject: [PATCH 048/823] zoom > 12 fixed --- lib/assets/javascripts/gfw/forest2000_tile_layer.js | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/assets/javascripts/gfw/forest2000_tile_layer.js b/lib/assets/javascripts/gfw/forest2000_tile_layer.js index 198aace54f..85edef3e87 100644 --- a/lib/assets/javascripts/gfw/forest2000_tile_layer.js +++ b/lib/assets/javascripts/gfw/forest2000_tile_layer.js @@ -113,6 +113,7 @@ Forest2000TileLayerThreshold.prototype.filter_tile = function(canvas) { var srcY = 256 / Math.pow(2, zsteps) * (coord.y % Math.pow(2, zsteps)); var srcW = 256 / Math.pow(2, zsteps); var srcH = 256 / Math.pow(2, zsteps); + ctx.clearRect(0, 0, 256, 256); ctx.drawImage(canvas.image, srcX, srcY, srcW, srcH, 0, 0, 256, 256); } else { try { From a53eeb9f8e9bdba90c3590e7c303c43f837bf94a Mon Sep 17 00:00:00 2001 From: Pedro Garcia Date: Thu, 5 Jun 2014 16:18:11 +0200 Subject: [PATCH 049/823] loading map fixed --- .../javascripts/countries/views/header.js | 147 ++++++++++-------- 1 file changed, 81 insertions(+), 66 deletions(-) diff --git a/app/assets/javascripts/countries/views/header.js b/app/assets/javascripts/countries/views/header.js index 8f78ff26de..0958679326 100644 --- a/app/assets/javascripts/countries/views/header.js +++ b/app/assets/javascripts/countries/views/header.js @@ -46,12 +46,12 @@ gfw.ui.view.leafletCanvasLayer = Backbone.View.extend({ initialize: function(options) { var self = this; - options = options || {layerName: ''}; + options = options || {layerName: '', mapOptions: {}}; this.layerOptions = new gfw.ui.model.layersOptions({layer: options.layerName}); _.extend(this, this.layerOptions.toJSON()); - this.layer = L.tileLayer.canvas(); + this.layer = L.tileLayer.canvas(options.mapOptions); this.layer.drawTile = function(canvas, tilePoint, zoom) { var xhr = new XMLHttpRequest(), @@ -168,6 +168,8 @@ gfw.ui.view.CountryHeader = cdb.core.View.extend({ _.extend(this, options); var self = this; + _.bindAll(this, '_cartodbLayerDone'); + // Cache this.$areaSelector = this.$('#areaSelector'); this.$selectorRemove = this.$('.selector-remove'); @@ -264,11 +266,6 @@ gfw.ui.view.CountryHeader = cdb.core.View.extend({ _renderMap: function(callback) { var self = this; - // Map & layers - this.cartodbLayer = {}; - this.forestLayer = {}; - this.map = {}; - this.map = new L.Map('countryMap', { center: [0, 0], zoom: 3, @@ -283,50 +280,36 @@ gfw.ui.view.CountryHeader = cdb.core.View.extend({ }); this.forestLayer = new gfw.ui.view.leafletCanvasLayer({ - layerName: 'forest2000' + layerName: 'forest2000', + mapOptions: { + opacity: 0 + } }).getLayer().addTo(this.map); - // Set country layer - cartodb.createLayer(this.map, { - user_name: 'wri-01', - type: 'cartodb', - cartodb_logo: false, - sublayers: [{ - sql: "SELECT * FROM country_mask" - }, { - sql: "SELECT * FROM gadm_1_all" - }] - }) - .addTo(this.map) - .done(function(layer) { - self.cartodbLayer = layer; - self.cartodbLayer.on('loading' , function() { - self.$map.removeClass('loaded'); - self.forestLayer.setOpacity(0); - }); - - self.cartodbLayer.on('load' , function() { - self.$map.addClass('loaded'); - self.forestLayer.setOpacity(1); - }); - - self.cartodbLayer.on('tileload' , function() { - // _.each(self.map._layers, function(layer) { - // console.log(layer._tilesToLoad); - // }); - }); + callback(); + }, - callback(); - }); + _removeCartodblayer: function() { + if (this.cartodbLayer) { + this.cartodbLayer.remove(); + } }, _displayCountry: function() { + var self = this; + + this.map.fitBounds(this.country.get('bounds')); + this._removeCartodblayer(); + this.$selectorRemove.hide(); this.$areaSelector.val(''); - this.map.fitBounds(this.country.get('bounds')); - this.cartodbLayer.getSubLayer(1).hide(); - this.cartodbLayer.getSubLayer(0) - .set({ + + cartodb.createLayer(this.map, { + user_name: 'wri-01', + type: 'cartodb', + cartodb_logo: false, + sublayers: [{ + sql: "SELECT * FROM country_mask", cartocss: "\ #country_mask {\ polygon-fill: #373442;\ @@ -341,14 +324,42 @@ gfw.ui.view.CountryHeader = cdb.core.View.extend({ line-width: 1;\ line-opacity: 1;\ }" - }); + }] + }) + .addTo(this.map) + .done(this._cartodbLayerDone); }, _displayArea: function(area) { - this.$selectorRemove.show(); + var self = this; + this.map.fitBounds(area.get('bounds'), {reset: true}); - this.cartodbLayer.getSubLayer(1) - .set({ + this._removeCartodblayer(); + + this.$selectorRemove.show(); + + cartodb.createLayer(this.map, { + user_name: 'wri-01', + type: 'cartodb', + cartodb_logo: false, + sublayers: [{ + sql: "SELECT * FROM country_mask", + cartocss: "\ + #country_mask {\ + polygon-fill: #373442;\ + polygon-opacity: 1;\ + line-color: #373442;\ + line-width: 1;\ + line-opacity: 1;\ + }\ + #country_mask[code='" + this.country.get('iso') + "'] {\ + polygon-opacity: 0;\ + line-color: #373442;\ + line-width: 1;\ + line-opacity: 1;\ + }" + }, { + sql: "SELECT * FROM gadm_1_all", cartocss: "\ #gadm_1_all {\ polygon-fill: #373442;\ @@ -366,26 +377,30 @@ gfw.ui.view.CountryHeader = cdb.core.View.extend({ line-opacity: 1;\ }\ }" - }) - .show(); + }] + }) + .addTo(this.map) + .done(this._cartodbLayerDone); + }, - this.cartodbLayer.getSubLayer(0) - .set({ - cartocss: "\ - #country_mask {\ - polygon-fill: #373442;\ - polygon-opacity: 1;\ - line-color: #373442;\ - line-width: 1;\ - line-opacity: 1;\ - }\ - #country_mask[code='" + this.country.get('iso') + "'] {\ - polygon-opacity: 0;\ - line-color: #373442;\ - line-width: 1;\ - line-opacity: 1;\ - }" - }); + _cartodbLayerDone: function(layer) { + var self = this; + + this.cartodbLayer = layer; + + this.cartodbLayer.on('loading' , function() { + self.$map.removeClass('loaded'); + self.forestLayer.setOpacity(0); + }); + + this.cartodbLayer.on('load' , function() { + self.$map.addClass('loaded'); + self.forestLayer.setOpacity(1); + }); + + this.cartodbLayer.on('tileload' , function() { + console.log(self.cartodbLayer._tilesToLoad); + }); }, _drawLossAndGain: function() { From d2746e096989fe151fc40019e26b58afa353d9e3 Mon Sep 17 00:00:00 2001 From: Pedro Garcia Date: Thu, 5 Jun 2014 16:22:26 +0200 Subject: [PATCH 050/823] hot fixed country map --- app/assets/javascripts/countries/views/header.js | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/app/assets/javascripts/countries/views/header.js b/app/assets/javascripts/countries/views/header.js index 0958679326..ac5399770e 100644 --- a/app/assets/javascripts/countries/views/header.js +++ b/app/assets/javascripts/countries/views/header.js @@ -135,6 +135,7 @@ gfw.ui.view.leafletCanvasLayer = Backbone.View.extend({ srcY = 256 / Math.pow(2, zsteps) * (coord.y % Math.pow(2, zsteps)), srcW = 256 / Math.pow(2, zsteps), srcH = 256 / Math.pow(2, zsteps); + ctx.clearRect(0, 0, 256, 256); ctx.drawImage(canvas.image, srcX, srcY, srcW, srcH, 0, 0, 256, 256); } else { @@ -397,10 +398,6 @@ gfw.ui.view.CountryHeader = cdb.core.View.extend({ self.$map.addClass('loaded'); self.forestLayer.setOpacity(1); }); - - this.cartodbLayer.on('tileload' , function() { - console.log(self.cartodbLayer._tilesToLoad); - }); }, _drawLossAndGain: function() { From d5ea177a47fc5322aea23c25f3c90cefd7fc3df1 Mon Sep 17 00:00:00 2001 From: Pedro Garcia Date: Thu, 5 Jun 2014 19:15:43 +0200 Subject: [PATCH 051/823] forest type text removed --- app/assets/javascripts/countries/views/show.js | 8 +++----- app/assets/javascripts/embed_countries.js | 2 +- app/assets/stylesheets/countries/show.scss | 12 +++++------- app/views/countries/show.html.erb | 5 +---- 4 files changed, 10 insertions(+), 17 deletions(-) diff --git a/app/assets/javascripts/countries/views/show.js b/app/assets/javascripts/countries/views/show.js index 6a9a482432..df72f10db5 100644 --- a/app/assets/javascripts/countries/views/show.js +++ b/app/assets/javascripts/countries/views/show.js @@ -269,14 +269,12 @@ gfw.ui.view.CountriesShow = cdb.core.View.extend({ sumData = _.reduce(data, function(memo, num){ return memo + num; }, 0), $countryForestType = $('.country-forests-type'); - if (sumData === 0) { - $countryForestType.find('.left-col').addClass('wide'); - $countryForestType.find('.forest-type-legends').hide(); - $countryForestType.find('.section-content').show(); + if (sumData !== 100) { + $countryForestType.find('.coming-soon').show(); return; } - $countryForestType.find('.section-content').show(); + $countryForestType.find('.forest-type-legends').show(); var width = 225, height = 225, diff --git a/app/assets/javascripts/embed_countries.js b/app/assets/javascripts/embed_countries.js index 7a83b0c6b2..3c6fa6bcd9 100644 --- a/app/assets/javascripts/embed_countries.js +++ b/app/assets/javascripts/embed_countries.js @@ -15,7 +15,7 @@ gfw.ui.view.CountriesEmbedOverview = cdb.core.View.extend({ el: document.body, events: { - 'click .graph_tab': '_updateGraph', + 'click .graph_tab': '_updateGraph' }, initialize: function() { diff --git a/app/assets/stylesheets/countries/show.scss b/app/assets/stylesheets/countries/show.scss index f3e0f1517f..5c0cfb6a71 100644 --- a/app/assets/stylesheets/countries/show.scss +++ b/app/assets/stylesheets/countries/show.scss @@ -503,7 +503,8 @@ .country-alerts, .country-production, .country-tenure, - .country-employment { + .country-employment, + .country-forests-type { .coming-soon { width: 100%; font-size: 16px; @@ -515,7 +516,8 @@ } } - .country-tenure { + .country-tenure, + .country-forests-type { .coming-soon { display: none; } @@ -625,17 +627,13 @@ } .country-forests-type { - .section-content { - display: none; - } - .forests-type-graph { margin-top: -25px; } .forest-type-legends { font-size: 14px; - display: inline-block; + display: none; margin-top: 10px; .legends-title { diff --git a/app/views/countries/show.html.erb b/app/views/countries/show.html.erb index 56161e526d..662a3bd739 100644 --- a/app/views/countries/show.html.erb +++ b/app/views/countries/show.html.erb @@ -105,11 +105,8 @@

    +
    No data available
    -

    - <%= @country['name'] %> has <%= extent_to_human(@country['extent']).downcase %> hectares of tree cover -

    -
    Forest Type:
      From 58e7aceba8872c94f930d661f1c7c348e77e7666 Mon Sep 17 00:00:00 2001 From: Pedro Garcia Date: Thu, 5 Jun 2014 19:21:14 +0200 Subject: [PATCH 052/823] hot fix --- app/assets/javascripts/countries/views/show.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/assets/javascripts/countries/views/show.js b/app/assets/javascripts/countries/views/show.js index df72f10db5..91475e8196 100644 --- a/app/assets/javascripts/countries/views/show.js +++ b/app/assets/javascripts/countries/views/show.js @@ -269,7 +269,7 @@ gfw.ui.view.CountriesShow = cdb.core.View.extend({ sumData = _.reduce(data, function(memo, num){ return memo + num; }, 0), $countryForestType = $('.country-forests-type'); - if (sumData !== 100) { + if (sumData === 0) { $countryForestType.find('.coming-soon').show(); return; } From 627aa525b7c7f84034e04a79605c1d2776e749e0 Mon Sep 17 00:00:00 2001 From: Pedro Garcia Date: Thu, 5 Jun 2014 19:36:15 +0200 Subject: [PATCH 053/823] fix donut percentage total --- app/assets/javascripts/countries/views/show.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/assets/javascripts/countries/views/show.js b/app/assets/javascripts/countries/views/show.js index 91475e8196..0c857a039d 100644 --- a/app/assets/javascripts/countries/views/show.js +++ b/app/assets/javascripts/countries/views/show.js @@ -274,6 +274,10 @@ gfw.ui.view.CountriesShow = cdb.core.View.extend({ return; } + if (sumData !== 100) { + data[2] = (100 - (data[0] + data[1])); + } + $countryForestType.find('.forest-type-legends').show(); var width = 225, From 7a82fee2c4eb6b7f3566c0f19ef47d2979b81457 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A1n=20P=C3=A9rez?= Date: Fri, 6 Jun 2014 12:00:13 +0200 Subject: [PATCH 054/823] umd intensity: map fix --- .../gfw/deforestation_tile_layer.js | 16 ++--- lib/assets/javascripts/gfw/gfw_lib.js.erb | 62 +++++++++---------- lib/assets/javascripts/gfw/map.js | 3 +- lib/assets/javascripts/gfw/ui/timeline.js | 1 + .../javascripts/gfw/ui/umd_options.js.erb | 2 +- 5 files changed, 42 insertions(+), 42 deletions(-) diff --git a/lib/assets/javascripts/gfw/deforestation_tile_layer.js b/lib/assets/javascripts/gfw/deforestation_tile_layer.js index c159ca5e1d..77a3bbb596 100644 --- a/lib/assets/javascripts/gfw/deforestation_tile_layer.js +++ b/lib/assets/javascripts/gfw/deforestation_tile_layer.js @@ -186,20 +186,20 @@ var Deforestation = function() { } config.BASELAYER = 'loss'; //hardcoded to make it work until we have other data layers switch (config.BASELAYER) { - case 'loss75' : var url = 'http://earthengine.google.org/static/hansen_2013/gfw_loss_year_75/' + z + '/' + x + '/' + y + '.png'; + case 'loss75': var url = 'http://earthengine.google.org/static/hansen_2013/gfw_loss_year_75/' + z + '/' + x + '/' + y + '.png'; break; - case 'loss50' : var url = 'http://earthengine.google.org/static/hansen_2013/gfw_loss_year_50/' + z + '/' + x + '/' + y + '.png'; + case 'loss50': var url = 'http://earthengine.google.org/static/hansen_2013/gfw_loss_year_50/' + z + '/' + x + '/' + y + '.png'; break; - case 'loss30' : var url = 'http://earthengine.google.org/static/hansen_2013/gfw_loss_year_30/' + z + '/' + x + '/' + y + '.png'; + case 'loss30': var url = 'http://earthengine.google.org/static/hansen_2013/gfw_loss_year_30/' + z + '/' + x + '/' + y + '.png'; break; - case 'loss25' : var url = 'http://earthengine.google.org/static/hansen_2013/gfw_loss_year_25/' + z + '/' + x + '/' + y + '.png'; + case 'loss25': var url = 'http://earthengine.google.org/static/hansen_2013/gfw_loss_year_25/' + z + '/' + x + '/' + y + '.png'; break; - case 'loss20' : var url = 'http://earthengine.google.org/static/hansen_2013/gfw_loss_year_20/' + z + '/' + x + '/' + y + '.png'; + case 'loss20': var url = 'http://earthengine.google.org/static/hansen_2013/gfw_loss_year_20/' + z + '/' + x + '/' + y + '.png'; break; - case 'loss15' : var url = 'http://earthengine.google.org/static/hansen_2013/gfw_loss_year_15/' + z + '/' + x + '/' + y + '.png'; + case 'loss15': var url = 'http://earthengine.google.org/static/hansen_2013/gfw_loss_year_15/' + z + '/' + x + '/' + y + '.png'; break; - case 'loss10' : - case 'loss' : + case 'loss10': + case 'loss': default: var url = 'http://earthengine.google.org/static/hansen_2013/gfw_loss_year/' + z + '/' + x + '/' + y + '.png'; } diff --git a/lib/assets/javascripts/gfw/gfw_lib.js.erb b/lib/assets/javascripts/gfw/gfw_lib.js.erb index c67755d323..ab5eecdb05 100644 --- a/lib/assets/javascripts/gfw/gfw_lib.js.erb +++ b/lib/assets/javascripts/gfw/gfw_lib.js.erb @@ -99,7 +99,6 @@ GFW.modules.app = function(gfw) { lng = parseFloat(map.getCenter().lng()).toFixed(6); this.$map_coordinates.html('Lat/Long: '+lat + ',' + lng); - publish('timeline:update_coordinates'); }, @@ -213,7 +212,7 @@ GFW.modules.app = function(gfw) { }); google.maps.event.addListener(map, 'center_changed', function() { - that.updateCoordinates(); + //that.updateCoordinates(); }); google.maps.event.addListener(map, 'zoom_changed', function() { @@ -227,8 +226,8 @@ GFW.modules.app = function(gfw) { }); google.maps.event.addListener(map, 'dragend', function() { - that.updateCoordinates(); - updateHash(); + //that.updateCoordinates(); + //updateHash(); }); google.maps.event.addDomListener(window, 'resize', function() { @@ -237,32 +236,32 @@ GFW.modules.app = function(gfw) { }); google.maps.event.addListener(map, 'click', function(event) { - that.closeInfowindows(); - - if (!that.specialLayer) { return; } - - var // get click coordinates - lat = event.latLng.lat(), - lng = event.latLng.lng(), - params = { lat: lat, lon: lng }, - url = "//<%= ENV['GFW_API_HOST'] %>/wdpa/sites"; - - executeAjax(url, params, { - success: function(sites) { - var site = null; - - if (sites && sites.length > 0) { - var site = sites[0]; - - that.protectedInfowindow.setContent(site); - that.protectedInfowindow.setPosition(event.latLng); - that.protectedInfowindow.open(); - } - }, - error: function(e) { - console.error('WDPA API call failed', e, url); - } - }); + // that.closeInfowindows(); + + // if (!that.specialLayer) { return; } + + // var // get click coordinates + // lat = event.latLng.lat(), + // lng = event.latLng.lng(), + // params = { lat: lat, lon: lng }, + // url = "//<%= ENV['GFW_API_HOST'] %>/wdpa/sites"; + + // executeAjax(url, params, { + // success: function(sites) { + // var site = null; + + // if (sites && sites.length > 0) { + // var site = sites[0]; + + // that.protectedInfowindow.setContent(site); + // that.protectedInfowindow.setPosition(event.latLng); + // that.protectedInfowindow.open(); + // } + // }, + // error: function(e) { + // console.error('WDPA API call failed', e, url); + // } + // }); }); google.maps.event.addListener(map, 'tilesloaded', this._mapLoaded); @@ -1045,7 +1044,6 @@ GFW.modules.app = function(gfw) { _loadBaseLayer: function() { var table_name = null; - debugger; if (this.currentBaseLayer.indexOf('loss') === 0) { if (config.mapLoaded && !this.time_layer_loss) { this._loadTimeLayerLoss(); @@ -1154,7 +1152,7 @@ GFW.modules.app = function(gfw) { if (content_data.hasOwnProperty(key)) { temp = content_data[key]; - delete content_data[key]; + delete content_data[key];x key = key.replace(/_/g,' '); //add spaces to key names content_data[key.charAt(0).toUpperCase() + key.substring(1)] = temp; //uppercase } diff --git a/lib/assets/javascripts/gfw/map.js b/lib/assets/javascripts/gfw/map.js index c84e059821..2df99e61fa 100644 --- a/lib/assets/javascripts/gfw/map.js +++ b/lib/assets/javascripts/gfw/map.js @@ -327,7 +327,7 @@ $(function() { Legend.show(); SearchBox.show(); Share.show(); - if (config.BASELAYER === 'loss') { + if (config.BASELAYER.indexOf('loss') === 0) { UmdOptions.show(); } @@ -365,6 +365,7 @@ $(function() { _setupListeners: function(){ var that = this; + if(that.model.get('state') === 'home'){ google.maps.event.addListener(map, 'click', function(event) { updateHash(); diff --git a/lib/assets/javascripts/gfw/ui/timeline.js b/lib/assets/javascripts/gfw/ui/timeline.js index 9593ee190b..27b58be473 100644 --- a/lib/assets/javascripts/gfw/ui/timeline.js +++ b/lib/assets/javascripts/gfw/ui/timeline.js @@ -48,6 +48,7 @@ gfw.ui.view.Timeline = cdb.core.View.extend({ _initSubscribes: function() { subscribe('timeline:update_coordinates', _.bind(function() { this.model.set('coordinates', map.getCenter()); + console.log(config.BASELAYER, '_initSubscribes_2') }, this)); subscribe('timeline:change_baselayer', _.bind(function(baselayer) { diff --git a/lib/assets/javascripts/gfw/ui/umd_options.js.erb b/lib/assets/javascripts/gfw/ui/umd_options.js.erb index 3b4748cdd7..11248e2ce4 100644 --- a/lib/assets/javascripts/gfw/ui/umd_options.js.erb +++ b/lib/assets/javascripts/gfw/ui/umd_options.js.erb @@ -189,9 +189,9 @@ gfw.ui.view.umdoptionsDialog = gfw.ui.view.Widget.extend({ _onClickApply: function(e) { e && e.preventDefault(); + config.BASELAYER = 'loss' + this.canopy; updateHash(); - GFW.app.updateBaseLayer(config.BASELAYER) this.hide(); }, From 5da3494a5bb769256ca7398038091281549f086c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A1n=20P=C3=A9rez?= Date: Tue, 10 Jun 2014 17:26:13 +0200 Subject: [PATCH 055/823] Revert "track share" This reverts commit 0eca1992fb1cb9ff20d614138003d87853b051ef. --- lib/assets/javascripts/gfw/ui/share.js.erb | 9 --------- 1 file changed, 9 deletions(-) diff --git a/lib/assets/javascripts/gfw/ui/share.js.erb b/lib/assets/javascripts/gfw/ui/share.js.erb index bfa9f99a5b..5467db2586 100644 --- a/lib/assets/javascripts/gfw/ui/share.js.erb +++ b/lib/assets/javascripts/gfw/ui/share.js.erb @@ -43,15 +43,6 @@ gfw.ui.view.Share = cdb.core.View.extend({ e.preventDefault(); this.share.show(); - this._track(); - }, - - _track: function() { - if ($('body').hasClass('home')) { - ga('send', 'event', 'Share', 'Map', config.BASELAYER); - } else { - ga('send', 'event', 'Share', 'Graphic', $('body').attr('class')); - } }, _toggle: function() { From 712ef9223dd00f103832465df5bfe5dfa3b841bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A1n=20P=C3=A9rez?= Date: Thu, 12 Jun 2014 10:55:57 +0200 Subject: [PATCH 056/823] deforestation and forest2000 tiles: new hansen tiles --- .../gfw/deforestation_tile_layer.js | 21 ++++++----- .../javascripts/gfw/forest2000_tile_layer.js | 37 +++++++++---------- 2 files changed, 28 insertions(+), 30 deletions(-) diff --git a/lib/assets/javascripts/gfw/deforestation_tile_layer.js b/lib/assets/javascripts/gfw/deforestation_tile_layer.js index 67bc07d8d3..610fffc9b8 100644 --- a/lib/assets/javascripts/gfw/deforestation_tile_layer.js +++ b/lib/assets/javascripts/gfw/deforestation_tile_layer.js @@ -113,6 +113,7 @@ DeforestationTileLayerThreshold.prototype.filter_tile = function(canvas, args) { var srcY = 256 / Math.pow(2, zsteps) * (coord.y % Math.pow(2, zsteps)); var srcW = 256 / Math.pow(2, zsteps); var srcH = 256 / Math.pow(2, zsteps); + ctx.clearRect(0, 0, 256, 256); ctx.drawImage(canvas.image, srcX, srcY, srcW, srcH, 0, 0, 256, 256); } else { try { @@ -135,30 +136,30 @@ var Deforestation = function() { var components = 4; //rgba var pixel_pos; var zoom = map.getZoom(); + var compressratio=1; + //var testarray=[]; for(var i=0; i < w; ++i) { for(var j=0; j < h; ++j) { var pixel_pos = (j*w + i) * components; - var yearLoss = image_data[pixel_pos]; - var intensity = image_data[pixel_pos + 1]; - + var yearLoss = image_data[pixel_pos+2]; + var intensity = image_data[pixel_pos]; yearLoss = 2000 + yearLoss; if (yearLoss >= year_start && yearLoss < year_end) { - //var c = (Math.ceil(3*intensity/255)); - var c = 3; + var c = (Math.ceil(3*intensity/255)); + //var c = 3; image_data[pixel_pos] = 220; image_data[pixel_pos + 1] = 102; image_data[pixel_pos + 2] = 153; if (zoom < 13) { - image_data[pixel_pos+ 3] = intensity < 10 ? 0: (12/zoom)*255*c/3; + image_data[pixel_pos+ 3] = compressratio*intensity; } else { - image_data[pixel_pos+ 3] = intensity < 10 ? 0: 255*c/3; + image_data[pixel_pos+ 3] = intensity; } } else { image_data[pixel_pos + 3] = 0; } - } } }; @@ -185,8 +186,8 @@ var Deforestation = function() { } } - var url = 'http://earthengine.google.org/static/hansen_2013/gfw_loss_year/' + z + '/' + x + '/' + y + '.png'; - + //var url = 'http://earthengine.google.org/static/hansen_2013/gfw_loss_year/' + z + '/' + x + '/' + y + '.png'; + var url = 'http://earthengine.google.org/static/hansen_2013/gfw_tree_loss_year_50/' + z + '/' + x + '/' + y + '.png'; xhr.onload = function () { var url = URL.createObjectURL(this.response), image = new Image(); diff --git a/lib/assets/javascripts/gfw/forest2000_tile_layer.js b/lib/assets/javascripts/gfw/forest2000_tile_layer.js index 85edef3e87..f57a35aca8 100644 --- a/lib/assets/javascripts/gfw/forest2000_tile_layer.js +++ b/lib/assets/javascripts/gfw/forest2000_tile_layer.js @@ -133,28 +133,24 @@ var Forest2000 = function() { function filter(image_data, w, h, year_start, year_end) { var components = 4; //rgba var pixel_pos; + var zoom = map.getZoom(); + var compressratio=1; + for(var i=0; i < w; ++i) { for(var j=0; j < h; ++j) { var pixel_pos = (j*w + i) * components; - //var yearLoss = image_data[pixel_pos]; - var intensity = image_data[pixel_pos + 3]; - var zoom = map.getZoom(); - - - var c = (Math.ceil(3*intensity/255)); - c = 3; - - image_data[pixel_pos] = 0; - image_data[pixel_pos + 1] = intensity*0.7; - image_data[pixel_pos + 2] = 0; - image_data[pixel_pos+ 3]=intensity*0.7 - // if (zoom < 13) { - // image_data[pixel_pos+ 3] = intensity < 10 ? 0: (12/zoom)*255*c/3; - // } else { - // image_data[pixel_pos+ 3] = intensity < 10 ? 0: 255*c/3; - // } - - + var intensity = image_data[pixel_pos+1]; + var c = (Math.ceil(3*intensity/255)); + //c = 3; + image_data[pixel_pos] = 60; + image_data[pixel_pos + 1] = 200; + image_data[pixel_pos + 2] = 0; + + if (zoom < 13) { + image_data[pixel_pos+ 3] = compressratio*intensity; + } else { + image_data[pixel_pos+ 3] = intensity; + } } } }; @@ -181,7 +177,8 @@ var Forest2000 = function() { } } - var url = 'http://earthengine.google.org/static/hansen_2013/tree_alpha/' + z + '/' + x + '/' + y + '.png'; + //var url = 'http://earthengine.google.org/static/hansen_2013/tree_alpha/' + z + '/' + x + '/' + y + '.png'; + var url = 'http://earthengine.google.org/static/hansen_2013/gfw_tree_loss_year_50/' + z + '/' + x + '/' + y + '.png'; xhr.onload = function () { var url = URL.createObjectURL(this.response), image = new Image(); From 2a445c2cf5e85615cc529d23df7b852548a32e9c Mon Sep 17 00:00:00 2001 From: Adrian Date: Mon, 16 Jun 2014 09:24:09 -0700 Subject: [PATCH 057/823] umd intensity: proper URLs --- .../javascripts/gfw/deforestation_tile_layer.js | 16 ++++++++-------- lib/assets/javascripts/gfw/gfw_lib.js.erb | 1 - 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/lib/assets/javascripts/gfw/deforestation_tile_layer.js b/lib/assets/javascripts/gfw/deforestation_tile_layer.js index c159ca5e1d..0dc1971382 100644 --- a/lib/assets/javascripts/gfw/deforestation_tile_layer.js +++ b/lib/assets/javascripts/gfw/deforestation_tile_layer.js @@ -184,23 +184,23 @@ var Deforestation = function() { x = Math.pow(2,z) - Math.abs(x); } } - config.BASELAYER = 'loss'; //hardcoded to make it work until we have other data layers + switch (config.BASELAYER) { - case 'loss75' : var url = 'http://earthengine.google.org/static/hansen_2013/gfw_loss_year_75/' + z + '/' + x + '/' + y + '.png'; + case 'loss75' : var url = 'http://earthengine.google.org/static/hansen_2013/gfw_tree_loss_year_75/' + z + '/' + x + '/' + y + '.png'; break; - case 'loss50' : var url = 'http://earthengine.google.org/static/hansen_2013/gfw_loss_year_50/' + z + '/' + x + '/' + y + '.png'; + case 'loss50' : var url = 'http://earthengine.google.org/static/hansen_2013/gfw_tree_loss_year_50/' + z + '/' + x + '/' + y + '.png'; break; - case 'loss30' : var url = 'http://earthengine.google.org/static/hansen_2013/gfw_loss_year_30/' + z + '/' + x + '/' + y + '.png'; + case 'loss30' : var url = 'http://earthengine.google.org/static/hansen_2013/gfw_tree_loss_year_30/' + z + '/' + x + '/' + y + '.png'; break; - case 'loss25' : var url = 'http://earthengine.google.org/static/hansen_2013/gfw_loss_year_25/' + z + '/' + x + '/' + y + '.png'; + case 'loss25' : var url = 'http://earthengine.google.org/static/hansen_2013/gfw_tree_loss_year_25/' + z + '/' + x + '/' + y + '.png'; break; - case 'loss20' : var url = 'http://earthengine.google.org/static/hansen_2013/gfw_loss_year_20/' + z + '/' + x + '/' + y + '.png'; + case 'loss20' : var url = 'http://earthengine.google.org/static/hansen_2013/gfw_tree_loss_year_20/' + z + '/' + x + '/' + y + '.png'; break; - case 'loss15' : var url = 'http://earthengine.google.org/static/hansen_2013/gfw_loss_year_15/' + z + '/' + x + '/' + y + '.png'; + case 'loss15' : var url = 'http://earthengine.google.org/static/hansen_2013/gfw_tree_loss_year_15/' + z + '/' + x + '/' + y + '.png'; break; case 'loss10' : case 'loss' : - default: var url = 'http://earthengine.google.org/static/hansen_2013/gfw_loss_year/' + z + '/' + x + '/' + y + '.png'; + default: var url = 'http://earthengine.google.org/static/hansen_2013/gfw_tree_loss_year_10/' + z + '/' + x + '/' + y + '.png'; } xhr.onload = function () { diff --git a/lib/assets/javascripts/gfw/gfw_lib.js.erb b/lib/assets/javascripts/gfw/gfw_lib.js.erb index c67755d323..c5947cd13d 100644 --- a/lib/assets/javascripts/gfw/gfw_lib.js.erb +++ b/lib/assets/javascripts/gfw/gfw_lib.js.erb @@ -1045,7 +1045,6 @@ GFW.modules.app = function(gfw) { _loadBaseLayer: function() { var table_name = null; - debugger; if (this.currentBaseLayer.indexOf('loss') === 0) { if (config.mapLoaded && !this.time_layer_loss) { this._loadTimeLayerLoss(); From bb99ba2647293ca3ce351c9417c670080d7c0b40 Mon Sep 17 00:00:00 2001 From: Adrian Date: Mon, 16 Jun 2014 12:31:35 -0700 Subject: [PATCH 058/823] umd intensity: fix umd config option --- lib/assets/javascripts/gfw/deforestation_tile_layer.js | 2 -- lib/assets/javascripts/gfw/ui/analysis.js.erb | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/assets/javascripts/gfw/deforestation_tile_layer.js b/lib/assets/javascripts/gfw/deforestation_tile_layer.js index 0dc1971382..2b95a67882 100644 --- a/lib/assets/javascripts/gfw/deforestation_tile_layer.js +++ b/lib/assets/javascripts/gfw/deforestation_tile_layer.js @@ -145,8 +145,6 @@ var Deforestation = function() { if (yearLoss >= year_start && yearLoss < year_end) { var c = (Math.ceil(3*intensity/255)); - c = 3; - image_data[pixel_pos] = 220; image_data[pixel_pos + 1] = 102; image_data[pixel_pos + 2] = 153; diff --git a/lib/assets/javascripts/gfw/ui/analysis.js.erb b/lib/assets/javascripts/gfw/ui/analysis.js.erb index 94633000f4..27956f48f4 100644 --- a/lib/assets/javascripts/gfw/ui/analysis.js.erb +++ b/lib/assets/javascripts/gfw/ui/analysis.js.erb @@ -891,7 +891,7 @@ gfw.ui.view.AnalysisInfo = gfw.ui.view.Widget.extend({ var that = this; var options = _.extend(this.model.toJSON(), { - umd: config.BASELAYER === 'loss', + umd: config.BASELAYER.indexOf('loss') === 0, imazon: config.BASELAYER === 'imazon', modis: config.BASELAYER === 'modis' }); From fc1d1c7fbacb2bc709f5debc07e9ebabceb58c42 Mon Sep 17 00:00:00 2001 From: Adrian Date: Mon, 16 Jun 2014 13:51:51 -0700 Subject: [PATCH 059/823] umd intensity: algorythm correction --- .../gfw/deforestation_tile_layer.js | 10 +++--- .../javascripts/gfw/forest2000_tile_layer.js | 31 +++++++++++-------- 2 files changed, 24 insertions(+), 17 deletions(-) diff --git a/lib/assets/javascripts/gfw/deforestation_tile_layer.js b/lib/assets/javascripts/gfw/deforestation_tile_layer.js index 2b95a67882..53372d974e 100644 --- a/lib/assets/javascripts/gfw/deforestation_tile_layer.js +++ b/lib/assets/javascripts/gfw/deforestation_tile_layer.js @@ -113,6 +113,7 @@ DeforestationTileLayerThreshold.prototype.filter_tile = function(canvas, args) { var srcY = 256 / Math.pow(2, zsteps) * (coord.y % Math.pow(2, zsteps)); var srcW = 256 / Math.pow(2, zsteps); var srcH = 256 / Math.pow(2, zsteps); + ctx.clearRect(0, 0, 256, 256); ctx.drawImage(canvas.image, srcX, srcY, srcW, srcH, 0, 0, 256, 256); } else { try { @@ -134,13 +135,14 @@ var Deforestation = function() { function filter(image_data, w, h, year_start, year_end) { var components = 4; //rgba var pixel_pos; + var compressratio = 1; var zoom = map.getZoom(); for(var i=0; i < w; ++i) { for(var j=0; j < h; ++j) { var pixel_pos = (j*w + i) * components; - var yearLoss = image_data[pixel_pos]; - var intensity = image_data[pixel_pos + 1]; + var yearLoss = image_data[pixel_pos+2]; + var intensity = image_data[pixel_pos]; yearLoss = 2000 + yearLoss; if (yearLoss >= year_start && yearLoss < year_end) { @@ -149,9 +151,9 @@ var Deforestation = function() { image_data[pixel_pos + 1] = 102; image_data[pixel_pos + 2] = 153; if (zoom < 13) { - image_data[pixel_pos+ 3] = intensity < 10 ? 0: (12/zoom)*255*c/3; + image_data[pixel_pos+ 3] = compressratio*intensity; } else { - image_data[pixel_pos+ 3] = intensity < 10 ? 0: 255*c/3; + image_data[pixel_pos+ 3] = intensity; } } else { image_data[pixel_pos + 3] = 0; diff --git a/lib/assets/javascripts/gfw/forest2000_tile_layer.js b/lib/assets/javascripts/gfw/forest2000_tile_layer.js index f57a35aca8..9718341e2b 100644 --- a/lib/assets/javascripts/gfw/forest2000_tile_layer.js +++ b/lib/assets/javascripts/gfw/forest2000_tile_layer.js @@ -138,19 +138,24 @@ var Forest2000 = function() { for(var i=0; i < w; ++i) { for(var j=0; j < h; ++j) { - var pixel_pos = (j*w + i) * components; - var intensity = image_data[pixel_pos+1]; - var c = (Math.ceil(3*intensity/255)); - //c = 3; - image_data[pixel_pos] = 60; - image_data[pixel_pos + 1] = 200; - image_data[pixel_pos + 2] = 0; - - if (zoom < 13) { - image_data[pixel_pos+ 3] = compressratio*intensity; - } else { - image_data[pixel_pos+ 3] = intensity; - } + var pixel_pos = (j*w + i) * components; + var yearLoss = image_data[pixel_pos+2]; + var intensity = image_data[pixel_pos]; + yearLoss = 2000 + yearLoss; + + if (yearLoss >= year_start && yearLoss < year_end) { + var c = (Math.ceil(3*intensity/255)); + image_data[pixel_pos] = 220; + image_data[pixel_pos + 1] = 102; + image_data[pixel_pos + 2] = 153; + if (zoom < 13) { + image_data[pixel_pos+ 3] = compressratio*intensity; + } else { + image_data[pixel_pos+ 3] = intensity; + } + } else { + image_data[pixel_pos + 3] = 0; + } } } }; From bda4d6db6f2defe1190079223952ec1776cb407f Mon Sep 17 00:00:00 2001 From: Adrian Date: Mon, 16 Jun 2014 14:58:18 -0700 Subject: [PATCH 060/823] umd options: fixed gain --- .../javascripts/gfw/forest2000_tile_layer.js | 31 ++++++++----------- 1 file changed, 13 insertions(+), 18 deletions(-) diff --git a/lib/assets/javascripts/gfw/forest2000_tile_layer.js b/lib/assets/javascripts/gfw/forest2000_tile_layer.js index 9718341e2b..b1ebe2051b 100644 --- a/lib/assets/javascripts/gfw/forest2000_tile_layer.js +++ b/lib/assets/javascripts/gfw/forest2000_tile_layer.js @@ -138,24 +138,19 @@ var Forest2000 = function() { for(var i=0; i < w; ++i) { for(var j=0; j < h; ++j) { - var pixel_pos = (j*w + i) * components; - var yearLoss = image_data[pixel_pos+2]; - var intensity = image_data[pixel_pos]; - yearLoss = 2000 + yearLoss; - - if (yearLoss >= year_start && yearLoss < year_end) { - var c = (Math.ceil(3*intensity/255)); - image_data[pixel_pos] = 220; - image_data[pixel_pos + 1] = 102; - image_data[pixel_pos + 2] = 153; - if (zoom < 13) { - image_data[pixel_pos+ 3] = compressratio*intensity; - } else { - image_data[pixel_pos+ 3] = intensity; - } - } else { - image_data[pixel_pos + 3] = 0; - } + var pixel_pos = (j*w + i) * components; + var intensity = image_data[pixel_pos+1]; + var c = (Math.ceil(3*intensity/255)); + //c = 3; + image_data[pixel_pos] = 60; + image_data[pixel_pos + 1] = 200; + image_data[pixel_pos + 2] = 0; + if (zoom < 13) { + image_data[pixel_pos+ 3] = compressratio*intensity; + } else { + image_data[pixel_pos+ 3] = intensity; + } + } } }; From 9c457ee2540dfe5bd27a7c9a147109b5dd371b04 Mon Sep 17 00:00:00 2001 From: Adrian Date: Mon, 16 Jun 2014 15:00:47 -0700 Subject: [PATCH 061/823] umd options: code clean --- lib/assets/javascripts/gfw/forest2000_tile_layer.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/assets/javascripts/gfw/forest2000_tile_layer.js b/lib/assets/javascripts/gfw/forest2000_tile_layer.js index b1ebe2051b..011360c888 100644 --- a/lib/assets/javascripts/gfw/forest2000_tile_layer.js +++ b/lib/assets/javascripts/gfw/forest2000_tile_layer.js @@ -141,7 +141,7 @@ var Forest2000 = function() { var pixel_pos = (j*w + i) * components; var intensity = image_data[pixel_pos+1]; var c = (Math.ceil(3*intensity/255)); - //c = 3; + image_data[pixel_pos] = 60; image_data[pixel_pos + 1] = 200; image_data[pixel_pos + 2] = 0; From 9c41a1eb27cc4b05fb08ea2c0595884797a2bd7f Mon Sep 17 00:00:00 2001 From: Adrian Date: Mon, 16 Jun 2014 15:00:52 -0700 Subject: [PATCH 062/823] umd options: code clean --- .../javascripts/gfw/forest2000_tile_layer.js | 25 +++++++++---------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/lib/assets/javascripts/gfw/forest2000_tile_layer.js b/lib/assets/javascripts/gfw/forest2000_tile_layer.js index 011360c888..62ac05e35c 100644 --- a/lib/assets/javascripts/gfw/forest2000_tile_layer.js +++ b/lib/assets/javascripts/gfw/forest2000_tile_layer.js @@ -138,19 +138,18 @@ var Forest2000 = function() { for(var i=0; i < w; ++i) { for(var j=0; j < h; ++j) { - var pixel_pos = (j*w + i) * components; - var intensity = image_data[pixel_pos+1]; - var c = (Math.ceil(3*intensity/255)); - - image_data[pixel_pos] = 60; - image_data[pixel_pos + 1] = 200; - image_data[pixel_pos + 2] = 0; - if (zoom < 13) { - image_data[pixel_pos+ 3] = compressratio*intensity; - } else { - image_data[pixel_pos+ 3] = intensity; - } - + var pixel_pos = (j*w + i) * components; + var intensity = image_data[pixel_pos+1]; + var c = (Math.ceil(3*intensity/255)); + + image_data[pixel_pos] = 60; + image_data[pixel_pos + 1] = 200; + image_data[pixel_pos + 2] = 0; + if (zoom < 13) { + image_data[pixel_pos+ 3] = compressratio*intensity; + } else { + image_data[pixel_pos+ 3] = intensity; + } } } }; From d6128fd9e7d03bc43cdf9cbcc2b244bfa0d8cc9a Mon Sep 17 00:00:00 2001 From: Adrian Date: Mon, 16 Jun 2014 15:45:36 -0700 Subject: [PATCH 063/823] umd intensity: fixed show/hide icon when loss --- lib/assets/javascripts/gfw/gfw_lib.js.erb | 8 ++++---- lib/assets/javascripts/gfw/map.js | 2 +- lib/assets/javascripts/gfw/ui/filter.js | 4 ++-- lib/assets/javascripts/gfw/ui/legend.js | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/lib/assets/javascripts/gfw/gfw_lib.js.erb b/lib/assets/javascripts/gfw/gfw_lib.js.erb index bc3d99547f..bdc3ed066c 100644 --- a/lib/assets/javascripts/gfw/gfw_lib.js.erb +++ b/lib/assets/javascripts/gfw/gfw_lib.js.erb @@ -1295,7 +1295,7 @@ GFW.modules.maplayer = function(gfw) { if (GFW.app.currentBaseLayer === slug) Legend.replace(this.layer.get('id'), this.layer.get('category_slug'), this.layer.get('category_name'), this.layer.get('title'), slug, this.layer.get('category_color'), this.layer.get('title_color'), subEvent); } else if (slug === 'umd_tree_loss_gain') { - if (umd_tree_loss_gain && GFW.app.currentBaseLayer === 'loss') umd_tree_loss_gain.set('visible', true); + if (umd_tree_loss_gain && GFW.app.currentBaseLayer.indexOf('loss') === 0) umd_tree_loss_gain.set('visible', true); var event = function() { that._toggleLayer(); @@ -1307,7 +1307,7 @@ GFW.modules.maplayer = function(gfw) { Filter.addForestLossFilters(this.layer.get('id'), slug, this.layer.get('category_name'), this.layer.get('title'), { clickEvent: event, source: this.layer.get('source'), category_color: this.layer.get('category_color'), color: this.layer.get('title_color'), subtitle: this.layer.get('subtitle') }); - if (GFW.app.currentBaseLayer === 'loss' || _.include(config.MAPOPTIONS.layers, 596)) Legend.toggleItem(this.layer.get('id'), this.layer.get('category_slug'), this.layer.get('category_name'), this.layer.get('title'), slug, this.layer.get('category_color'), this.layer.get('title_color'), subEvent); + if (GFW.app.currentBaseLayer.indexOf('loss') === 0 || _.include(config.MAPOPTIONS.layers, 596)) Legend.toggleItem(this.layer.get('id'), this.layer.get('category_slug'), this.layer.get('category_name'), this.layer.get('title'), slug, this.layer.get('category_color'), this.layer.get('title_color'), subEvent); } else if (slug === 'loss') { var event = function() { that._toggleLayer(); @@ -1315,7 +1315,7 @@ GFW.modules.maplayer = function(gfw) { Filter.addForestLossFilter(this.layer.get('id'), slug, this.layer.get('category_name'), this.layer.get('title'), { clickEvent: event, source: this.layer.get('source'), category_color: this.layer.get('category_color'), color: this.layer.get('title_color'), subtitle: this.layer.get('subtitle') }); - if (GFW.app.currentBaseLayer === 'loss') { + if (GFW.app.currentBaseLayer.indexOf('loss') === 0) { if (filters && _.include(filters, this.layer.get('id'))) { if (GFW.app) { GFW.app.loadLayer(this.layer); @@ -1550,7 +1550,7 @@ GFW.modules.maplayer = function(gfw) { } else { Analysis.show(); } - if (slug === 'loss' || GFW.app.currentBaseLayer === 'loss') { + if (slug === 'loss' || GFW.app.currentBaseLayer.indexOf('loss') === 0) { UmdOptions.show(); } else { UmdOptions.hide(); diff --git a/lib/assets/javascripts/gfw/map.js b/lib/assets/javascripts/gfw/map.js index c84e059821..e8c9101637 100644 --- a/lib/assets/javascripts/gfw/map.js +++ b/lib/assets/javascripts/gfw/map.js @@ -327,7 +327,7 @@ $(function() { Legend.show(); SearchBox.show(); Share.show(); - if (config.BASELAYER === 'loss') { + if (config.BASELAYER.indexOf('loss') === 0) { UmdOptions.show(); } diff --git a/lib/assets/javascripts/gfw/ui/filter.js b/lib/assets/javascripts/gfw/ui/filter.js index f4f40ca796..10eb183a96 100644 --- a/lib/assets/javascripts/gfw/ui/filter.js +++ b/lib/assets/javascripts/gfw/ui/filter.js @@ -230,7 +230,7 @@ var Filter = (function() { $layerItem = $(layerItemTemplate({ name: name, id: id, slug: slug, category: cat, disabled: disabled, source: source, color: color, subtitle: subtitle })); - if (slug === 'loss' && config.BASELAYER === 'loss' || slug === 'forestgain' && _.include(config.MAPOPTIONS.layers, 596)) { + if (slug === 'loss' && config.BASELAYER.indexOf('loss') === 0 || slug === 'forestgain' && _.include(config.MAPOPTIONS.layers, 596)) { $layerItem.find('.checkbox').addClass('checked'); var color = $layerItem.find('.checkbox').attr('data-color'); @@ -271,7 +271,7 @@ var Filter = (function() { $layerItem = $(layerItemTemplate({ name: name, id: id, slug:slug, category: cat, disabled: disabled, source: source, subtitle: subtitle })); - if (config.BASELAYER === 'loss' || _.include(config.MAPOPTIONS.layers, 596)) { + if (config.BASELAYER.indexOf('loss') === 0 || _.include(config.MAPOPTIONS.layers, 596)) { $layerItem.find('.radio').addClass('checked'); } else { $layerItem.find('.extra').hide(); diff --git a/lib/assets/javascripts/gfw/ui/legend.js b/lib/assets/javascripts/gfw/ui/legend.js index 52615a3a54..d58e408e06 100644 --- a/lib/assets/javascripts/gfw/ui/legend.js +++ b/lib/assets/javascripts/gfw/ui/legend.js @@ -282,7 +282,7 @@ gfw.ui.view.Legend = gfw.ui.view.Widget.extend({ this.$content.find('li.' + item.attributes.category).fadeIn(250); - if (config.BASELAYER === 'loss') { + if (config.BASELAYER.indexOf('loss') === 0) { this.$content.find('li#' + item.attributes.cat_id + '.loss').fadeIn(250); } From 05c6b19386ac36d97baa3b50af1b9da528a4b23b Mon Sep 17 00:00:00 2001 From: David Gonzalez Date: Mon, 16 Jun 2014 16:01:25 -0700 Subject: [PATCH 064/823] compression in canvas layers --- .../gfw/deforestation_tile_layer.js | 4 +- .../javascripts/gfw/forest2000_tile_layer.js | 37 +++++++++++++++---- 2 files changed, 32 insertions(+), 9 deletions(-) diff --git a/lib/assets/javascripts/gfw/deforestation_tile_layer.js b/lib/assets/javascripts/gfw/deforestation_tile_layer.js index 53372d974e..95ef6e4f22 100644 --- a/lib/assets/javascripts/gfw/deforestation_tile_layer.js +++ b/lib/assets/javascripts/gfw/deforestation_tile_layer.js @@ -135,8 +135,8 @@ var Deforestation = function() { function filter(image_data, w, h, year_start, year_end) { var components = 4; //rgba var pixel_pos; - var compressratio = 1; var zoom = map.getZoom(); + var compressratio = 12/zoom; for(var i=0; i < w; ++i) { for(var j=0; j < h; ++j) { @@ -203,6 +203,8 @@ var Deforestation = function() { default: var url = 'http://earthengine.google.org/static/hansen_2013/gfw_tree_loss_year_10/' + z + '/' + x + '/' + y + '.png'; } + console.log(url); + xhr.onload = function () { var url = URL.createObjectURL(this.response), image = new Image(); diff --git a/lib/assets/javascripts/gfw/forest2000_tile_layer.js b/lib/assets/javascripts/gfw/forest2000_tile_layer.js index 62ac05e35c..005d5c33aa 100644 --- a/lib/assets/javascripts/gfw/forest2000_tile_layer.js +++ b/lib/assets/javascripts/gfw/forest2000_tile_layer.js @@ -135,20 +135,22 @@ var Forest2000 = function() { var pixel_pos; var zoom = map.getZoom(); var compressratio=1; - + var threshold=Number(config.BASELAYER.slice(4)); + (!threshold)? threshold=10:true; for(var i=0; i < w; ++i) { for(var j=0; j < h; ++j) { var pixel_pos = (j*w + i) * components; var intensity = image_data[pixel_pos+1]; - var c = (Math.ceil(3*intensity/255)); + //var c = (Math.ceil(3*intensity/255)); - image_data[pixel_pos] = 60; - image_data[pixel_pos + 1] = 200; - image_data[pixel_pos + 2] = 0; + image_data[pixel_pos] = 151; + image_data[pixel_pos + 1] = 189; + image_data[pixel_pos + 2] = 61; + if (zoom < 13) { - image_data[pixel_pos+ 3] = compressratio*intensity; + image_data[pixel_pos+ 3] = compressratio*intensity*0.8; } else { - image_data[pixel_pos+ 3] = intensity; + image_data[pixel_pos+ 3] = intensity*0.8; } } } @@ -177,7 +179,26 @@ var Forest2000 = function() { } //var url = 'http://earthengine.google.org/static/hansen_2013/tree_alpha/' + z + '/' + x + '/' + y + '.png'; - var url = 'http://earthengine.google.org/static/hansen_2013/gfw_tree_loss_year_50/' + z + '/' + x + '/' + y + '.png'; + //var url = 'http://earthengine.google.org/static/hansen_2013/gfw_tree_loss_year_50/' + z + '/' + x + '/' + y + '.png'; + switch (config.BASELAYER) { + case 'loss75' : var url = 'http://earthengine.google.org/static/hansen_2013/gfw_tree_loss_year_75/' + z + '/' + x + '/' + y + '.png'; + break; + case 'loss50' : var url = 'http://earthengine.google.org/static/hansen_2013/gfw_tree_loss_year_50/' + z + '/' + x + '/' + y + '.png'; + break; + case 'loss30' : var url = 'http://earthengine.google.org/static/hansen_2013/gfw_tree_loss_year_30/' + z + '/' + x + '/' + y + '.png'; + break; + case 'loss25' : var url = 'http://earthengine.google.org/static/hansen_2013/gfw_tree_loss_year_25/' + z + '/' + x + '/' + y + '.png'; + break; + case 'loss20' : var url = 'http://earthengine.google.org/static/hansen_2013/gfw_tree_loss_year_20/' + z + '/' + x + '/' + y + '.png'; + break; + case 'loss15' : var url = 'http://earthengine.google.org/static/hansen_2013/gfw_tree_loss_year_15/' + z + '/' + x + '/' + y + '.png'; + break; + case 'loss10' : + case 'loss' : + default: var url = 'http://earthengine.google.org/static/hansen_2013/gfw_tree_loss_year_10/' + z + '/' + x + '/' + y + '.png'; + } + + xhr.onload = function () { var url = URL.createObjectURL(this.response), image = new Image(); From e21abb1d61f396a995a6d1754e2d7a2afe21e67e Mon Sep 17 00:00:00 2001 From: Adrian Date: Mon, 16 Jun 2014 16:52:03 -0700 Subject: [PATCH 065/823] umd intensity: hide buttons, change on slide and click option --- app/assets/stylesheets/home.scss | 4 +++- lib/assets/javascripts/gfw/deforestation_tile_layer.js | 2 -- lib/assets/javascripts/gfw/ui/umd_options.js.erb | 9 +++++++-- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/app/assets/stylesheets/home.scss b/app/assets/stylesheets/home.scss index 73cf1ceb57..e4ffb605bf 100644 --- a/app/assets/stylesheets/home.scss +++ b/app/assets/stylesheets/home.scss @@ -1542,7 +1542,8 @@ body { top: 205px; width: 400px; min-height: 250px; - padding: 20px; + padding: 20px 20px 0px 20px; + z-index: 1000; .close { display: block; position: absolute; @@ -1684,6 +1685,7 @@ body { &:last-child { float: right; margin-bottom: 0; + display: none; /*hardcoded, change on click*/ } } ul { diff --git a/lib/assets/javascripts/gfw/deforestation_tile_layer.js b/lib/assets/javascripts/gfw/deforestation_tile_layer.js index 95ef6e4f22..3bd5d7c28e 100644 --- a/lib/assets/javascripts/gfw/deforestation_tile_layer.js +++ b/lib/assets/javascripts/gfw/deforestation_tile_layer.js @@ -203,8 +203,6 @@ var Deforestation = function() { default: var url = 'http://earthengine.google.org/static/hansen_2013/gfw_tree_loss_year_10/' + z + '/' + x + '/' + y + '.png'; } - console.log(url); - xhr.onload = function () { var url = URL.createObjectURL(this.response), image = new Image(); diff --git a/lib/assets/javascripts/gfw/ui/umd_options.js.erb b/lib/assets/javascripts/gfw/ui/umd_options.js.erb index 3b4748cdd7..c22afa2143 100644 --- a/lib/assets/javascripts/gfw/ui/umd_options.js.erb +++ b/lib/assets/javascripts/gfw/ui/umd_options.js.erb @@ -86,6 +86,7 @@ gfw.ui.view.umdoptionsDialog = gfw.ui.view.Widget.extend({ 'click .apply': '_onClickApply', 'change #canopy_slider': '_onChangeSlider', 'input #canopy_slider': '_onDragSlider', + 'mouseup #canopy_slider': '_onDragEndSlider', 'click .slider_option': '_onClickOption' }, @@ -115,6 +116,9 @@ gfw.ui.view.umdoptionsDialog = gfw.ui.view.Widget.extend({ this._paintRange(document.getElementById('canopy_slider').value) }, + _onDragEndSlider: function() { + this._onClickApply(); + }, _onClickOption: function(e) { switch($(e.target).data('option')) { case 0: @@ -143,6 +147,7 @@ gfw.ui.view.umdoptionsDialog = gfw.ui.view.Widget.extend({ this._paintRange(75) break; } + this._onClickApply(); }, _paintRange: function(slider_val) { @@ -184,7 +189,7 @@ gfw.ui.view.umdoptionsDialog = gfw.ui.view.Widget.extend({ $('.visible_range').css('width', 60); break; } - document.getElementById('canopy_slider').value = slider_val; + document.getElementById('canopy_slider').value = slider_val; }, _onClickApply: function(e) { @@ -192,7 +197,7 @@ gfw.ui.view.umdoptionsDialog = gfw.ui.view.Widget.extend({ config.BASELAYER = 'loss' + this.canopy; updateHash(); GFW.app.updateBaseLayer(config.BASELAYER) - this.hide(); + //this.hide(); }, show: function() { From 67c206f6d71edf2d0a6829905c1e29611c4bfe3b Mon Sep 17 00:00:00 2001 From: Adrian Date: Mon, 16 Jun 2014 17:11:05 -0700 Subject: [PATCH 066/823] umd intensity: avoid reloading current canopy --- lib/assets/javascripts/gfw/ui/umd_options.js.erb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/assets/javascripts/gfw/ui/umd_options.js.erb b/lib/assets/javascripts/gfw/ui/umd_options.js.erb index c22afa2143..c5af6b40dc 100644 --- a/lib/assets/javascripts/gfw/ui/umd_options.js.erb +++ b/lib/assets/javascripts/gfw/ui/umd_options.js.erb @@ -117,9 +117,11 @@ gfw.ui.view.umdoptionsDialog = gfw.ui.view.Widget.extend({ }, _onDragEndSlider: function() { + if (config.BASELAYER.slice(-2) == this.canopy) return; this._onClickApply(); }, _onClickOption: function(e) { + if (config.BASELAYER.slice(-2) == $(e.target).data('option')) return; switch($(e.target).data('option')) { case 0: case 100: From 782442975ef71eba2478553011a7d11dcab35271 Mon Sep 17 00:00:00 2001 From: David Gonzalez Date: Mon, 16 Jun 2014 17:25:12 -0700 Subject: [PATCH 067/823] linear compression in canvas layers --- lib/assets/javascripts/gfw/deforestation_tile_layer.js | 5 ++--- lib/assets/javascripts/gfw/forest2000_tile_layer.js | 4 ++-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/lib/assets/javascripts/gfw/deforestation_tile_layer.js b/lib/assets/javascripts/gfw/deforestation_tile_layer.js index 95ef6e4f22..b3f9b2dea9 100644 --- a/lib/assets/javascripts/gfw/deforestation_tile_layer.js +++ b/lib/assets/javascripts/gfw/deforestation_tile_layer.js @@ -136,7 +136,7 @@ var Deforestation = function() { var components = 4; //rgba var pixel_pos; var zoom = map.getZoom(); - var compressratio = 12/zoom; + var t=64; for(var i=0; i < w; ++i) { for(var j=0; j < h; ++j) { @@ -151,7 +151,7 @@ var Deforestation = function() { image_data[pixel_pos + 1] = 102; image_data[pixel_pos + 2] = 153; if (zoom < 13) { - image_data[pixel_pos+ 3] = compressratio*intensity; + intensity>0? image_data[pixel_pos+ 3] = intensity+t-(t*intensity)/256 : image_data[pixel_pos+ 3] =0; } else { image_data[pixel_pos+ 3] = intensity; } @@ -203,7 +203,6 @@ var Deforestation = function() { default: var url = 'http://earthengine.google.org/static/hansen_2013/gfw_tree_loss_year_10/' + z + '/' + x + '/' + y + '.png'; } - console.log(url); xhr.onload = function () { var url = URL.createObjectURL(this.response), diff --git a/lib/assets/javascripts/gfw/forest2000_tile_layer.js b/lib/assets/javascripts/gfw/forest2000_tile_layer.js index 005d5c33aa..ea7b8e1b6b 100644 --- a/lib/assets/javascripts/gfw/forest2000_tile_layer.js +++ b/lib/assets/javascripts/gfw/forest2000_tile_layer.js @@ -134,7 +134,7 @@ var Forest2000 = function() { var components = 4; //rgba var pixel_pos; var zoom = map.getZoom(); - var compressratio=1; + var t=64; var threshold=Number(config.BASELAYER.slice(4)); (!threshold)? threshold=10:true; for(var i=0; i < w; ++i) { @@ -148,7 +148,7 @@ var Forest2000 = function() { image_data[pixel_pos + 2] = 61; if (zoom < 13) { - image_data[pixel_pos+ 3] = compressratio*intensity*0.8; + intensity>0? image_data[pixel_pos+ 3] = (intensity+t-(t*intensity)/256)*0.8 : image_data[pixel_pos+ 3] =0; } else { image_data[pixel_pos+ 3] = intensity*0.8; } From 9439843bb0f33aad9895e1536764e5a5e09983fa Mon Sep 17 00:00:00 2001 From: Adrian Date: Mon, 16 Jun 2014 19:07:52 -0700 Subject: [PATCH 068/823] umd options: config canopy and ui changes --- app/assets/stylesheets/home.scss | 2 +- .../gfw/deforestation_tile_layer.js | 23 +++++----------- .../javascripts/gfw/forest2000_tile_layer.js | 27 +++++-------------- lib/assets/javascripts/gfw/gfw_lib.js.erb | 10 ++++--- lib/assets/javascripts/gfw/helpers.js.erb | 3 ++- lib/assets/javascripts/gfw/ui/timeline.js | 2 +- .../javascripts/gfw/ui/umd_options.js.erb | 3 +-- 7 files changed, 25 insertions(+), 45 deletions(-) diff --git a/app/assets/stylesheets/home.scss b/app/assets/stylesheets/home.scss index e4ffb605bf..ec2f3d0a72 100644 --- a/app/assets/stylesheets/home.scss +++ b/app/assets/stylesheets/home.scss @@ -995,7 +995,7 @@ body { .umd_options_control { position: absolute; - top: 130px; + top: 168px; left: 15px; width: 37px; height: 37px; diff --git a/lib/assets/javascripts/gfw/deforestation_tile_layer.js b/lib/assets/javascripts/gfw/deforestation_tile_layer.js index b3f9b2dea9..542edc842c 100644 --- a/lib/assets/javascripts/gfw/deforestation_tile_layer.js +++ b/lib/assets/javascripts/gfw/deforestation_tile_layer.js @@ -185,25 +185,14 @@ var Deforestation = function() { } } - switch (config.BASELAYER) { - case 'loss75' : var url = 'http://earthengine.google.org/static/hansen_2013/gfw_tree_loss_year_75/' + z + '/' + x + '/' + y + '.png'; - break; - case 'loss50' : var url = 'http://earthengine.google.org/static/hansen_2013/gfw_tree_loss_year_50/' + z + '/' + x + '/' + y + '.png'; - break; - case 'loss30' : var url = 'http://earthengine.google.org/static/hansen_2013/gfw_tree_loss_year_30/' + z + '/' + x + '/' + y + '.png'; - break; - case 'loss25' : var url = 'http://earthengine.google.org/static/hansen_2013/gfw_tree_loss_year_25/' + z + '/' + x + '/' + y + '.png'; - break; - case 'loss20' : var url = 'http://earthengine.google.org/static/hansen_2013/gfw_tree_loss_year_20/' + z + '/' + x + '/' + y + '.png'; - break; - case 'loss15' : var url = 'http://earthengine.google.org/static/hansen_2013/gfw_tree_loss_year_15/' + z + '/' + x + '/' + y + '.png'; - break; - case 'loss10' : - case 'loss' : - default: var url = 'http://earthengine.google.org/static/hansen_2013/gfw_tree_loss_year_10/' + z + '/' + x + '/' + y + '.png'; + if (config.canopy_choice) { + var url = 'http://earthengine.google.org/static/hansen_2013/gfw_tree_loss_year_'+ config.canopy_choice +'/' + z + '/' + x + '/' + y + '.png'; + } else { + var canopy = (config.BASELAYER === 'loss') ? '10' : config.BASELAYER.slice(-2); + var url = 'http://earthengine.google.org/static/hansen_2013/gfw_tree_loss_year_'+ canopy +'/' + z + '/' + x + '/' + y + '.png'; + config.canopy_choice = canopy; } - xhr.onload = function () { var url = URL.createObjectURL(this.response), image = new Image(); diff --git a/lib/assets/javascripts/gfw/forest2000_tile_layer.js b/lib/assets/javascripts/gfw/forest2000_tile_layer.js index ea7b8e1b6b..eef6541750 100644 --- a/lib/assets/javascripts/gfw/forest2000_tile_layer.js +++ b/lib/assets/javascripts/gfw/forest2000_tile_layer.js @@ -178,27 +178,14 @@ var Forest2000 = function() { } } - //var url = 'http://earthengine.google.org/static/hansen_2013/tree_alpha/' + z + '/' + x + '/' + y + '.png'; - //var url = 'http://earthengine.google.org/static/hansen_2013/gfw_tree_loss_year_50/' + z + '/' + x + '/' + y + '.png'; - switch (config.BASELAYER) { - case 'loss75' : var url = 'http://earthengine.google.org/static/hansen_2013/gfw_tree_loss_year_75/' + z + '/' + x + '/' + y + '.png'; - break; - case 'loss50' : var url = 'http://earthengine.google.org/static/hansen_2013/gfw_tree_loss_year_50/' + z + '/' + x + '/' + y + '.png'; - break; - case 'loss30' : var url = 'http://earthengine.google.org/static/hansen_2013/gfw_tree_loss_year_30/' + z + '/' + x + '/' + y + '.png'; - break; - case 'loss25' : var url = 'http://earthengine.google.org/static/hansen_2013/gfw_tree_loss_year_25/' + z + '/' + x + '/' + y + '.png'; - break; - case 'loss20' : var url = 'http://earthengine.google.org/static/hansen_2013/gfw_tree_loss_year_20/' + z + '/' + x + '/' + y + '.png'; - break; - case 'loss15' : var url = 'http://earthengine.google.org/static/hansen_2013/gfw_tree_loss_year_15/' + z + '/' + x + '/' + y + '.png'; - break; - case 'loss10' : - case 'loss' : - default: var url = 'http://earthengine.google.org/static/hansen_2013/gfw_tree_loss_year_10/' + z + '/' + x + '/' + y + '.png'; + if (config.canopy_choice) { + var url = 'http://earthengine.google.org/static/hansen_2013/gfw_tree_loss_year_'+ config.canopy_choice +'/' + z + '/' + x + '/' + y + '.png'; + } else { + var canopy = (config.BASELAYER === 'loss') ? '10' : config.BASELAYER.slice(-2); + var url = 'http://earthengine.google.org/static/hansen_2013/gfw_tree_loss_year_'+ canopy +'/' + z + '/' + x + '/' + y + '.png'; + config.canopy_choice = canopy; } - - + xhr.onload = function () { var url = URL.createObjectURL(this.response), image = new Image(); diff --git a/lib/assets/javascripts/gfw/gfw_lib.js.erb b/lib/assets/javascripts/gfw/gfw_lib.js.erb index bdc3ed066c..fc7d0d25f5 100644 --- a/lib/assets/javascripts/gfw/gfw_lib.js.erb +++ b/lib/assets/javascripts/gfw/gfw_lib.js.erb @@ -760,6 +760,13 @@ GFW.modules.app = function(gfw) { }, updateBaseLayer: function(baselayer) { + if (config.canopy_choice && baselayer.indexOf('loss') === 0) { + baselayer = 'loss' + config.canopy_choice; + if (cover_extent.attributes['visible']) { + GFW.app._removeForest2000Layer(); + GFW.app._renderForest2000Layer(); + } + } this.currentBaseLayer = config.BASELAYER = baselayer; this._toggleTimeLayer(); this._loadBaseLayer(); @@ -1448,8 +1455,6 @@ GFW.modules.maplayer = function(gfw) { if (visible) { GFW.app.addLayer(this.layer); ga('send', 'event', 'Filter', 'Toggle', slug); - - if (cover_extent.attributes['visible']) GFW.app._refreshForest2000Layer(); } else { GFW.app.removeLayer(this.layer); } @@ -1461,7 +1466,6 @@ GFW.modules.maplayer = function(gfw) { GFW.app.updateBaseLayer(slug); ga('send', 'event', 'Filter', 'Toggle', slug); - if (cover_extent.attributes['visible']) GFW.app._refreshForest2000Layer(); } else { GFW.app.updateBaseLayer(null); diff --git a/lib/assets/javascripts/gfw/helpers.js.erb b/lib/assets/javascripts/gfw/helpers.js.erb index 311eb23550..74ff0a9af1 100644 --- a/lib/assets/javascripts/gfw/helpers.js.erb +++ b/lib/assets/javascripts/gfw/helpers.js.erb @@ -10,7 +10,8 @@ var config = { MONTHNAMES: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"], MONTHNAMES_SHORT: ["JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEC"], QUARTERNAMES: ["JAN - MAR", "APR - JUN", "JUL - SEP", "OCT - DEC"], - mapLoaded: false + mapLoaded: false, + canopy_choice: false }; config.MAPOPTIONS = { diff --git a/lib/assets/javascripts/gfw/ui/timeline.js b/lib/assets/javascripts/gfw/ui/timeline.js index 9593ee190b..a72f714b0a 100644 --- a/lib/assets/javascripts/gfw/ui/timeline.js +++ b/lib/assets/javascripts/gfw/ui/timeline.js @@ -108,12 +108,12 @@ gfw.ui.view.Timeline = cdb.core.View.extend({ _toggleBaselayer: function() { var that = this; - var baselayer = this.model.get('baselayer'); if (baselayer.indexOf('loss') === 0) { that.loss_timeline.show(); } else { + $('.umdoptions_dialog').hide(); that.loss_timeline.hide( (baselayer === null ) ) } diff --git a/lib/assets/javascripts/gfw/ui/umd_options.js.erb b/lib/assets/javascripts/gfw/ui/umd_options.js.erb index c5af6b40dc..10d2e5625d 100644 --- a/lib/assets/javascripts/gfw/ui/umd_options.js.erb +++ b/lib/assets/javascripts/gfw/ui/umd_options.js.erb @@ -50,12 +50,10 @@ gfw.ui.view.UmdOptions = cdb.core.View.extend({ show: function() { this.model.set('hidden', false); - $('.share_control').addClass('umd_options') }, hide: function() { this.model.set('hidden', true); - $('.share_control').removeClass('umd_options') }, render: function() { @@ -197,6 +195,7 @@ gfw.ui.view.umdoptionsDialog = gfw.ui.view.Widget.extend({ _onClickApply: function(e) { e && e.preventDefault(); config.BASELAYER = 'loss' + this.canopy; + config.canopy_choice = this.canopy; updateHash(); GFW.app.updateBaseLayer(config.BASELAYER) //this.hide(); From 314c16a6531ad05691c02a515dd02ec164cc8e62 Mon Sep 17 00:00:00 2001 From: David Gonzalez Date: Mon, 16 Jun 2014 20:02:36 -0700 Subject: [PATCH 069/823] exponential compression in loss layer --- app/assets/javascripts/application.js | 1 + lib/assets/javascripts/gfw/deforestation_tile_layer.js | 5 ++++- lib/assets/javascripts/gfw/forest2000_tile_layer.js | 5 +++-- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js index 8ddcd67cef..af35c9ce07 100644 --- a/app/assets/javascripts/application.js +++ b/app/assets/javascripts/application.js @@ -3,6 +3,7 @@ //= require gfw //= require geojson //= require gfw/helpers +//= require d3.v3.min $(function() { try { diff --git a/lib/assets/javascripts/gfw/deforestation_tile_layer.js b/lib/assets/javascripts/gfw/deforestation_tile_layer.js index 542edc842c..79ce3a59c6 100644 --- a/lib/assets/javascripts/gfw/deforestation_tile_layer.js +++ b/lib/assets/javascripts/gfw/deforestation_tile_layer.js @@ -137,6 +137,8 @@ var Deforestation = function() { var pixel_pos; var zoom = map.getZoom(); var t=64; + var myscale=d3.scale.pow().exponent(1/zoom).domain([0,256]).range([0,256]); + console.log(10 , myscale(10), 1/zoom) for(var i=0; i < w; ++i) { for(var j=0; j < h; ++j) { @@ -151,7 +153,8 @@ var Deforestation = function() { image_data[pixel_pos + 1] = 102; image_data[pixel_pos + 2] = 153; if (zoom < 13) { - intensity>0? image_data[pixel_pos+ 3] = intensity+t-(t*intensity)/256 : image_data[pixel_pos+ 3] =0; + //intensity>0? image_data[pixel_pos+ 3] = intensity+t-(t*intensity)/256 : image_data[pixel_pos+ 3] =0; + image_data[pixel_pos+ 3]=myscale(intensity); } else { image_data[pixel_pos+ 3] = intensity; } diff --git a/lib/assets/javascripts/gfw/forest2000_tile_layer.js b/lib/assets/javascripts/gfw/forest2000_tile_layer.js index eef6541750..787881349f 100644 --- a/lib/assets/javascripts/gfw/forest2000_tile_layer.js +++ b/lib/assets/javascripts/gfw/forest2000_tile_layer.js @@ -134,7 +134,7 @@ var Forest2000 = function() { var components = 4; //rgba var pixel_pos; var zoom = map.getZoom(); - var t=64; + var t=0; var threshold=Number(config.BASELAYER.slice(4)); (!threshold)? threshold=10:true; for(var i=0; i < w; ++i) { @@ -148,7 +148,8 @@ var Forest2000 = function() { image_data[pixel_pos + 2] = 61; if (zoom < 13) { - intensity>0? image_data[pixel_pos+ 3] = (intensity+t-(t*intensity)/256)*0.8 : image_data[pixel_pos+ 3] =0; + //intensity>0? image_data[pixel_pos+ 3] = (intensity+t-(t*intensity)/256)*0.8 : image_data[pixel_pos+ 3] =0; + image_data[pixel_pos+ 3] = intensity*0.8; } else { image_data[pixel_pos+ 3] = intensity*0.8; } From 5c55a5752d7f1e62abc428e2661d5ee8b8557353 Mon Sep 17 00:00:00 2001 From: David Gonzalez Date: Mon, 16 Jun 2014 20:18:29 -0700 Subject: [PATCH 070/823] cleanup --- lib/assets/javascripts/gfw/deforestation_tile_layer.js | 1 - lib/assets/javascripts/gfw/forest2000_tile_layer.js | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/assets/javascripts/gfw/deforestation_tile_layer.js b/lib/assets/javascripts/gfw/deforestation_tile_layer.js index 79ce3a59c6..224055985e 100644 --- a/lib/assets/javascripts/gfw/deforestation_tile_layer.js +++ b/lib/assets/javascripts/gfw/deforestation_tile_layer.js @@ -138,7 +138,6 @@ var Deforestation = function() { var zoom = map.getZoom(); var t=64; var myscale=d3.scale.pow().exponent(1/zoom).domain([0,256]).range([0,256]); - console.log(10 , myscale(10), 1/zoom) for(var i=0; i < w; ++i) { for(var j=0; j < h; ++j) { diff --git a/lib/assets/javascripts/gfw/forest2000_tile_layer.js b/lib/assets/javascripts/gfw/forest2000_tile_layer.js index 787881349f..4afd72405d 100644 --- a/lib/assets/javascripts/gfw/forest2000_tile_layer.js +++ b/lib/assets/javascripts/gfw/forest2000_tile_layer.js @@ -135,8 +135,8 @@ var Forest2000 = function() { var pixel_pos; var zoom = map.getZoom(); var t=0; - var threshold=Number(config.BASELAYER.slice(4)); - (!threshold)? threshold=10:true; + //var threshold=Number(config.BASELAYER.slice(4)); + //(!threshold)? threshold=10:true; for(var i=0; i < w; ++i) { for(var j=0; j < h; ++j) { var pixel_pos = (j*w + i) * components; From 5312cccb1d6afb445172629fa25749969d8498c7 Mon Sep 17 00:00:00 2001 From: David Gonzalez Date: Mon, 16 Jun 2014 20:40:13 -0700 Subject: [PATCH 071/823] no ramp legend in extent layer --- lib/assets/javascripts/gfw/ui/legend.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/assets/javascripts/gfw/ui/legend.js b/lib/assets/javascripts/gfw/ui/legend.js index d58e408e06..87bcba7c30 100644 --- a/lib/assets/javascripts/gfw/ui/legend.js +++ b/lib/assets/javascripts/gfw/ui/legend.js @@ -258,10 +258,10 @@ gfw.ui.view.Legend = gfw.ui.view.Widget.extend({ }); if (slug == 'forest2000') { - item.attributes.legend = 'Percent Tree Cover'; - item.attributes.legend_class = 'tree'; + //item.attributes.legend = 'Percent Tree Cover'; + //item.attributes.legend_class = 'tree'; - item.attributes.colors = [{ color: '#9DD89D', title: '25-50%' }, { color: '#4EC54E', title: '50-75%' }, { color: '#00B300', title: '75-100%' } ]; + //item.attributes.colors = [{ color: '#9DD89D', title: '25-50%' }, { color: '#4EC54E', title: '50-75%' }, { color: '#00B300', title: '75-100%' } ]; } else if (slug == 'pantropical') { item.attributes.legend = "Total Biomass (Mg C Ha-1)"; item.attributes.legend_class = ''; From 0e6fb12261cde9f1a606583c360efea8cba3fd45 Mon Sep 17 00:00:00 2001 From: pedro Date: Tue, 17 Jun 2014 17:09:43 +0200 Subject: [PATCH 072/823] requirejs rails --- Gemfile | 1 + Gemfile.lock | 3 + app/assets/javascripts/home.js | 53 +- app/views/layouts/home.html.erb | 55 + config/requirejs.yml | 8 + vendor/assets/javascripts/backbone.js | 1608 ++++ vendor/assets/javascripts/jquery.js | 10308 ++++++++++++++++++++++ vendor/assets/javascripts/minpubsub.js | 96 +- vendor/assets/javascripts/underscore.js | 1399 +++ 9 files changed, 13491 insertions(+), 40 deletions(-) create mode 100644 app/views/layouts/home.html.erb create mode 100644 config/requirejs.yml create mode 100644 vendor/assets/javascripts/backbone.js create mode 100644 vendor/assets/javascripts/jquery.js create mode 100644 vendor/assets/javascripts/underscore.js diff --git a/Gemfile b/Gemfile index 5fa71ea209..cf4120e6fd 100644 --- a/Gemfile +++ b/Gemfile @@ -13,6 +13,7 @@ gem 'carrierwave' gem 'fog' gem 'rmagick', :require => false gem 'unf' +gem 'requirejs-rails' group :development, :test do gem 'rspec-rails', '~> 2.0' diff --git a/Gemfile.lock b/Gemfile.lock index fe4b86566f..d2662da5bd 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -184,6 +184,8 @@ GEM rb-fsevent (0.9.4) rb-inotify (0.9.4) ffi (>= 0.5.0) + requirejs-rails (0.9.3) + railties (>= 3.1.1) rmagick (2.13.2) rspec-core (2.14.8) rspec-expectations (2.14.5) @@ -280,6 +282,7 @@ DEPENDENCIES pg rails (= 4.0.4) rails_12factor + requirejs-rails rmagick rspec-rails (~> 2.0) sass-rails (~> 4.0.0) diff --git a/app/assets/javascripts/home.js b/app/assets/javascripts/home.js index 50ae1a80c2..a5163228fb 100644 --- a/app/assets/javascripts/home.js +++ b/app/assets/javascripts/home.js @@ -1,39 +1,14 @@ -//= require jquery-migrate-1.2.1.min -//= require jquery-ui-1.10.4.custom.min -//= require class -//= require backbone.cartodb -//= require jquery.easing.1.3 -//= require jquery.tipsy -//= require jquery.qtip.min -//= require imgLiquid.min -//= require wax.g.min -//= require cartodb-gmapsv3 -//= require minpubsub -//= require markerclusterer_compiled -//= require url.min -//= require gfw/deforestation_tile_layer -//= require gfw/forest2000_tile_layer -//= require gfw/canvas_tile_layer -//= require gfw/static_grid_layer_imazon -//= require gfw/static_grid_layer -//= require gfw/gfw_lib -//= require gfw/grid_layer -//= require gfw/marker -//= require gfw/ui/widget -//= require gfw/ui/sourcewindow -//= require gfw/ui/infowindow -//= require gfw/ui/protected-infowindow -//= require gfw/ui/legend -//= require gfw/ui/search -//= require gfw/ui/layer_selector -//= require gfw/ui/analysis -//= require gfw/ui/share -//= require gfw/ui/filter -//= require gfw/ui/circle -//= require gfw/ui/timeline -//= require gfw/ui/timeline/timeline_loss -//= require gfw/ui/timeline/timeline_modis -//= require gfw/ui/timeline/timeline_forma -//= require gfw/ui/timeline/timeline_imazon -//= require gfw/ui/timeline/timeline_fires -//= require gfw/map +// Application entry point +require([ + 'app', + 'router', + 'presenter', + 'mediator', + 'backbone' +], function (app, router, presenter, mediator, Backbone) { + console.log('Main entry point...', app); + if (!Backbone.History.started) { + console.log('Backbone.history.start'); + Backbone.history.start({pushState: true}); + } +}); \ No newline at end of file diff --git a/app/views/layouts/home.html.erb b/app/views/layouts/home.html.erb new file mode 100644 index 0000000000..754f0f6fe5 --- /dev/null +++ b/app/views/layouts/home.html.erb @@ -0,0 +1,55 @@ + + + + + + + + + Global Forest Watch + + + + + + + + + + + + <%= favicon_link_tag 'favicon.ico' %> + <%= stylesheet_link_tag "application", :media => "all" %> + <%= controller_stylesheet_link_tag %> + <%= javascript_include_tag "modernizr-2.6.2.min" %> + <%= csrf_meta_tags %> + + +"> + +
      +
      + + <%= render 'shared/navbar' %> +
      +
      +
      + <%= yield :title %> + <%= yield :inner_header %> +
      + <%= yield :filters %> +
      + +
      + <%= yield %> +
      + + <%= render 'shared/footer' %> + + <%= requirejs_include_tag "home" %> + <%= yield :js %> + <%= render 'shared/js_templates' %> + <%= render 'shared/js_footer' %> + + + \ No newline at end of file diff --git a/config/requirejs.yml b/config/requirejs.yml new file mode 100644 index 0000000000..1bfce056dc --- /dev/null +++ b/config/requirejs.yml @@ -0,0 +1,8 @@ +modules: + - name: 'home' + +paths: + jquery: "jquery" + underscore: "underscore" + backbone: "backbone" + mps: "minpubsub" \ No newline at end of file diff --git a/vendor/assets/javascripts/backbone.js b/vendor/assets/javascripts/backbone.js new file mode 100644 index 0000000000..24a550a0ad --- /dev/null +++ b/vendor/assets/javascripts/backbone.js @@ -0,0 +1,1608 @@ +// Backbone.js 1.1.2 + +// (c) 2010-2014 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors +// Backbone may be freely distributed under the MIT license. +// For all details and documentation: +// http://backbonejs.org + +(function(root, factory) { + + // Set up Backbone appropriately for the environment. Start with AMD. + if (typeof define === 'function' && define.amd) { + define(['underscore', 'jquery', 'exports'], function(_, $, exports) { + // Export global even in AMD case in case this script is loaded with + // others that may still expect a global Backbone. + root.Backbone = factory(root, exports, _, $); + }); + + // Next for Node.js or CommonJS. jQuery may not be needed as a module. + } else if (typeof exports !== 'undefined') { + var _ = require('underscore'); + factory(root, exports, _); + + // Finally, as a browser global. + } else { + root.Backbone = factory(root, {}, root._, (root.jQuery || root.Zepto || root.ender || root.$)); + } + +}(this, function(root, Backbone, _, $) { + + // Initial Setup + // ------------- + + // Save the previous value of the `Backbone` variable, so that it can be + // restored later on, if `noConflict` is used. + var previousBackbone = root.Backbone; + + // Create local references to array methods we'll want to use later. + var array = []; + var push = array.push; + var slice = array.slice; + var splice = array.splice; + + // Current version of the library. Keep in sync with `package.json`. + Backbone.VERSION = '1.1.2'; + + // For Backbone's purposes, jQuery, Zepto, Ender, or My Library (kidding) owns + // the `$` variable. + Backbone.$ = $; + + // Runs Backbone.js in *noConflict* mode, returning the `Backbone` variable + // to its previous owner. Returns a reference to this Backbone object. + Backbone.noConflict = function() { + root.Backbone = previousBackbone; + return this; + }; + + // Turn on `emulateHTTP` to support legacy HTTP servers. Setting this option + // will fake `"PATCH"`, `"PUT"` and `"DELETE"` requests via the `_method` parameter and + // set a `X-Http-Method-Override` header. + Backbone.emulateHTTP = false; + + // Turn on `emulateJSON` to support legacy servers that can't deal with direct + // `application/json` requests ... will encode the body as + // `application/x-www-form-urlencoded` instead and will send the model in a + // form param named `model`. + Backbone.emulateJSON = false; + + // Backbone.Events + // --------------- + + // A module that can be mixed in to *any object* in order to provide it with + // custom events. You may bind with `on` or remove with `off` callback + // functions to an event; `trigger`-ing an event fires all callbacks in + // succession. + // + // var object = {}; + // _.extend(object, Backbone.Events); + // object.on('expand', function(){ alert('expanded'); }); + // object.trigger('expand'); + // + var Events = Backbone.Events = { + + // Bind an event to a `callback` function. Passing `"all"` will bind + // the callback to all events fired. + on: function(name, callback, context) { + if (!eventsApi(this, 'on', name, [callback, context]) || !callback) return this; + this._events || (this._events = {}); + var events = this._events[name] || (this._events[name] = []); + events.push({callback: callback, context: context, ctx: context || this}); + return this; + }, + + // Bind an event to only be triggered a single time. After the first time + // the callback is invoked, it will be removed. + once: function(name, callback, context) { + if (!eventsApi(this, 'once', name, [callback, context]) || !callback) return this; + var self = this; + var once = _.once(function() { + self.off(name, once); + callback.apply(this, arguments); + }); + once._callback = callback; + return this.on(name, once, context); + }, + + // Remove one or many callbacks. If `context` is null, removes all + // callbacks with that function. If `callback` is null, removes all + // callbacks for the event. If `name` is null, removes all bound + // callbacks for all events. + off: function(name, callback, context) { + var retain, ev, events, names, i, l, j, k; + if (!this._events || !eventsApi(this, 'off', name, [callback, context])) return this; + if (!name && !callback && !context) { + this._events = void 0; + return this; + } + names = name ? [name] : _.keys(this._events); + for (i = 0, l = names.length; i < l; i++) { + name = names[i]; + if (events = this._events[name]) { + this._events[name] = retain = []; + if (callback || context) { + for (j = 0, k = events.length; j < k; j++) { + ev = events[j]; + if ((callback && callback !== ev.callback && callback !== ev.callback._callback) || + (context && context !== ev.context)) { + retain.push(ev); + } + } + } + if (!retain.length) delete this._events[name]; + } + } + + return this; + }, + + // Trigger one or many events, firing all bound callbacks. Callbacks are + // passed the same arguments as `trigger` is, apart from the event name + // (unless you're listening on `"all"`, which will cause your callback to + // receive the true name of the event as the first argument). + trigger: function(name) { + if (!this._events) return this; + var args = slice.call(arguments, 1); + if (!eventsApi(this, 'trigger', name, args)) return this; + var events = this._events[name]; + var allEvents = this._events.all; + if (events) triggerEvents(events, args); + if (allEvents) triggerEvents(allEvents, arguments); + return this; + }, + + // Tell this object to stop listening to either specific events ... or + // to every object it's currently listening to. + stopListening: function(obj, name, callback) { + var listeningTo = this._listeningTo; + if (!listeningTo) return this; + var remove = !name && !callback; + if (!callback && typeof name === 'object') callback = this; + if (obj) (listeningTo = {})[obj._listenId] = obj; + for (var id in listeningTo) { + obj = listeningTo[id]; + obj.off(name, callback, this); + if (remove || _.isEmpty(obj._events)) delete this._listeningTo[id]; + } + return this; + } + + }; + + // Regular expression used to split event strings. + var eventSplitter = /\s+/; + + // Implement fancy features of the Events API such as multiple event + // names `"change blur"` and jQuery-style event maps `{change: action}` + // in terms of the existing API. + var eventsApi = function(obj, action, name, rest) { + if (!name) return true; + + // Handle event maps. + if (typeof name === 'object') { + for (var key in name) { + obj[action].apply(obj, [key, name[key]].concat(rest)); + } + return false; + } + + // Handle space separated event names. + if (eventSplitter.test(name)) { + var names = name.split(eventSplitter); + for (var i = 0, l = names.length; i < l; i++) { + obj[action].apply(obj, [names[i]].concat(rest)); + } + return false; + } + + return true; + }; + + // A difficult-to-believe, but optimized internal dispatch function for + // triggering events. Tries to keep the usual cases speedy (most internal + // Backbone events have 3 arguments). + var triggerEvents = function(events, args) { + var ev, i = -1, l = events.length, a1 = args[0], a2 = args[1], a3 = args[2]; + switch (args.length) { + case 0: while (++i < l) (ev = events[i]).callback.call(ev.ctx); return; + case 1: while (++i < l) (ev = events[i]).callback.call(ev.ctx, a1); return; + case 2: while (++i < l) (ev = events[i]).callback.call(ev.ctx, a1, a2); return; + case 3: while (++i < l) (ev = events[i]).callback.call(ev.ctx, a1, a2, a3); return; + default: while (++i < l) (ev = events[i]).callback.apply(ev.ctx, args); return; + } + }; + + var listenMethods = {listenTo: 'on', listenToOnce: 'once'}; + + // Inversion-of-control versions of `on` and `once`. Tell *this* object to + // listen to an event in another object ... keeping track of what it's + // listening to. + _.each(listenMethods, function(implementation, method) { + Events[method] = function(obj, name, callback) { + var listeningTo = this._listeningTo || (this._listeningTo = {}); + var id = obj._listenId || (obj._listenId = _.uniqueId('l')); + listeningTo[id] = obj; + if (!callback && typeof name === 'object') callback = this; + obj[implementation](name, callback, this); + return this; + }; + }); + + // Aliases for backwards compatibility. + Events.bind = Events.on; + Events.unbind = Events.off; + + // Allow the `Backbone` object to serve as a global event bus, for folks who + // want global "pubsub" in a convenient place. + _.extend(Backbone, Events); + + // Backbone.Model + // -------------- + + // Backbone **Models** are the basic data object in the framework -- + // frequently representing a row in a table in a database on your server. + // A discrete chunk of data and a bunch of useful, related methods for + // performing computations and transformations on that data. + + // Create a new model with the specified attributes. A client id (`cid`) + // is automatically generated and assigned for you. + var Model = Backbone.Model = function(attributes, options) { + var attrs = attributes || {}; + options || (options = {}); + this.cid = _.uniqueId('c'); + this.attributes = {}; + if (options.collection) this.collection = options.collection; + if (options.parse) attrs = this.parse(attrs, options) || {}; + attrs = _.defaults({}, attrs, _.result(this, 'defaults')); + this.set(attrs, options); + this.changed = {}; + this.initialize.apply(this, arguments); + }; + + // Attach all inheritable methods to the Model prototype. + _.extend(Model.prototype, Events, { + + // A hash of attributes whose current and previous value differ. + changed: null, + + // The value returned during the last failed validation. + validationError: null, + + // The default name for the JSON `id` attribute is `"id"`. MongoDB and + // CouchDB users may want to set this to `"_id"`. + idAttribute: 'id', + + // Initialize is an empty function by default. Override it with your own + // initialization logic. + initialize: function(){}, + + // Return a copy of the model's `attributes` object. + toJSON: function(options) { + return _.clone(this.attributes); + }, + + // Proxy `Backbone.sync` by default -- but override this if you need + // custom syncing semantics for *this* particular model. + sync: function() { + return Backbone.sync.apply(this, arguments); + }, + + // Get the value of an attribute. + get: function(attr) { + return this.attributes[attr]; + }, + + // Get the HTML-escaped value of an attribute. + escape: function(attr) { + return _.escape(this.get(attr)); + }, + + // Returns `true` if the attribute contains a value that is not null + // or undefined. + has: function(attr) { + return this.get(attr) != null; + }, + + // Set a hash of model attributes on the object, firing `"change"`. This is + // the core primitive operation of a model, updating the data and notifying + // anyone who needs to know about the change in state. The heart of the beast. + set: function(key, val, options) { + var attr, attrs, unset, changes, silent, changing, prev, current; + if (key == null) return this; + + // Handle both `"key", value` and `{key: value}` -style arguments. + if (typeof key === 'object') { + attrs = key; + options = val; + } else { + (attrs = {})[key] = val; + } + + options || (options = {}); + + // Run validation. + if (!this._validate(attrs, options)) return false; + + // Extract attributes and options. + unset = options.unset; + silent = options.silent; + changes = []; + changing = this._changing; + this._changing = true; + + if (!changing) { + this._previousAttributes = _.clone(this.attributes); + this.changed = {}; + } + current = this.attributes, prev = this._previousAttributes; + + // Check for changes of `id`. + if (this.idAttribute in attrs) this.id = attrs[this.idAttribute]; + + // For each `set` attribute, update or delete the current value. + for (attr in attrs) { + val = attrs[attr]; + if (!_.isEqual(current[attr], val)) changes.push(attr); + if (!_.isEqual(prev[attr], val)) { + this.changed[attr] = val; + } else { + delete this.changed[attr]; + } + unset ? delete current[attr] : current[attr] = val; + } + + // Trigger all relevant attribute changes. + if (!silent) { + if (changes.length) this._pending = options; + for (var i = 0, l = changes.length; i < l; i++) { + this.trigger('change:' + changes[i], this, current[changes[i]], options); + } + } + + // You might be wondering why there's a `while` loop here. Changes can + // be recursively nested within `"change"` events. + if (changing) return this; + if (!silent) { + while (this._pending) { + options = this._pending; + this._pending = false; + this.trigger('change', this, options); + } + } + this._pending = false; + this._changing = false; + return this; + }, + + // Remove an attribute from the model, firing `"change"`. `unset` is a noop + // if the attribute doesn't exist. + unset: function(attr, options) { + return this.set(attr, void 0, _.extend({}, options, {unset: true})); + }, + + // Clear all attributes on the model, firing `"change"`. + clear: function(options) { + var attrs = {}; + for (var key in this.attributes) attrs[key] = void 0; + return this.set(attrs, _.extend({}, options, {unset: true})); + }, + + // Determine if the model has changed since the last `"change"` event. + // If you specify an attribute name, determine if that attribute has changed. + hasChanged: function(attr) { + if (attr == null) return !_.isEmpty(this.changed); + return _.has(this.changed, attr); + }, + + // Return an object containing all the attributes that have changed, or + // false if there are no changed attributes. Useful for determining what + // parts of a view need to be updated and/or what attributes need to be + // persisted to the server. Unset attributes will be set to undefined. + // You can also pass an attributes object to diff against the model, + // determining if there *would be* a change. + changedAttributes: function(diff) { + if (!diff) return this.hasChanged() ? _.clone(this.changed) : false; + var val, changed = false; + var old = this._changing ? this._previousAttributes : this.attributes; + for (var attr in diff) { + if (_.isEqual(old[attr], (val = diff[attr]))) continue; + (changed || (changed = {}))[attr] = val; + } + return changed; + }, + + // Get the previous value of an attribute, recorded at the time the last + // `"change"` event was fired. + previous: function(attr) { + if (attr == null || !this._previousAttributes) return null; + return this._previousAttributes[attr]; + }, + + // Get all of the attributes of the model at the time of the previous + // `"change"` event. + previousAttributes: function() { + return _.clone(this._previousAttributes); + }, + + // Fetch the model from the server. If the server's representation of the + // model differs from its current attributes, they will be overridden, + // triggering a `"change"` event. + fetch: function(options) { + options = options ? _.clone(options) : {}; + if (options.parse === void 0) options.parse = true; + var model = this; + var success = options.success; + options.success = function(resp) { + if (!model.set(model.parse(resp, options), options)) return false; + if (success) success(model, resp, options); + model.trigger('sync', model, resp, options); + }; + wrapError(this, options); + return this.sync('read', this, options); + }, + + // Set a hash of model attributes, and sync the model to the server. + // If the server returns an attributes hash that differs, the model's + // state will be `set` again. + save: function(key, val, options) { + var attrs, method, xhr, attributes = this.attributes; + + // Handle both `"key", value` and `{key: value}` -style arguments. + if (key == null || typeof key === 'object') { + attrs = key; + options = val; + } else { + (attrs = {})[key] = val; + } + + options = _.extend({validate: true}, options); + + // If we're not waiting and attributes exist, save acts as + // `set(attr).save(null, opts)` with validation. Otherwise, check if + // the model will be valid when the attributes, if any, are set. + if (attrs && !options.wait) { + if (!this.set(attrs, options)) return false; + } else { + if (!this._validate(attrs, options)) return false; + } + + // Set temporary attributes if `{wait: true}`. + if (attrs && options.wait) { + this.attributes = _.extend({}, attributes, attrs); + } + + // After a successful server-side save, the client is (optionally) + // updated with the server-side state. + if (options.parse === void 0) options.parse = true; + var model = this; + var success = options.success; + options.success = function(resp) { + // Ensure attributes are restored during synchronous saves. + model.attributes = attributes; + var serverAttrs = model.parse(resp, options); + if (options.wait) serverAttrs = _.extend(attrs || {}, serverAttrs); + if (_.isObject(serverAttrs) && !model.set(serverAttrs, options)) { + return false; + } + if (success) success(model, resp, options); + model.trigger('sync', model, resp, options); + }; + wrapError(this, options); + + method = this.isNew() ? 'create' : (options.patch ? 'patch' : 'update'); + if (method === 'patch') options.attrs = attrs; + xhr = this.sync(method, this, options); + + // Restore attributes. + if (attrs && options.wait) this.attributes = attributes; + + return xhr; + }, + + // Destroy this model on the server if it was already persisted. + // Optimistically removes the model from its collection, if it has one. + // If `wait: true` is passed, waits for the server to respond before removal. + destroy: function(options) { + options = options ? _.clone(options) : {}; + var model = this; + var success = options.success; + + var destroy = function() { + model.trigger('destroy', model, model.collection, options); + }; + + options.success = function(resp) { + if (options.wait || model.isNew()) destroy(); + if (success) success(model, resp, options); + if (!model.isNew()) model.trigger('sync', model, resp, options); + }; + + if (this.isNew()) { + options.success(); + return false; + } + wrapError(this, options); + + var xhr = this.sync('delete', this, options); + if (!options.wait) destroy(); + return xhr; + }, + + // Default URL for the model's representation on the server -- if you're + // using Backbone's restful methods, override this to change the endpoint + // that will be called. + url: function() { + var base = + _.result(this, 'urlRoot') || + _.result(this.collection, 'url') || + urlError(); + if (this.isNew()) return base; + return base.replace(/([^\/])$/, '$1/') + encodeURIComponent(this.id); + }, + + // **parse** converts a response into the hash of attributes to be `set` on + // the model. The default implementation is just to pass the response along. + parse: function(resp, options) { + return resp; + }, + + // Create a new model with identical attributes to this one. + clone: function() { + return new this.constructor(this.attributes); + }, + + // A model is new if it has never been saved to the server, and lacks an id. + isNew: function() { + return !this.has(this.idAttribute); + }, + + // Check if the model is currently in a valid state. + isValid: function(options) { + return this._validate({}, _.extend(options || {}, { validate: true })); + }, + + // Run validation against the next complete set of model attributes, + // returning `true` if all is well. Otherwise, fire an `"invalid"` event. + _validate: function(attrs, options) { + if (!options.validate || !this.validate) return true; + attrs = _.extend({}, this.attributes, attrs); + var error = this.validationError = this.validate(attrs, options) || null; + if (!error) return true; + this.trigger('invalid', this, error, _.extend(options, {validationError: error})); + return false; + } + + }); + + // Underscore methods that we want to implement on the Model. + var modelMethods = ['keys', 'values', 'pairs', 'invert', 'pick', 'omit']; + + // Mix in each Underscore method as a proxy to `Model#attributes`. + _.each(modelMethods, function(method) { + Model.prototype[method] = function() { + var args = slice.call(arguments); + args.unshift(this.attributes); + return _[method].apply(_, args); + }; + }); + + // Backbone.Collection + // ------------------- + + // If models tend to represent a single row of data, a Backbone Collection is + // more analagous to a table full of data ... or a small slice or page of that + // table, or a collection of rows that belong together for a particular reason + // -- all of the messages in this particular folder, all of the documents + // belonging to this particular author, and so on. Collections maintain + // indexes of their models, both in order, and for lookup by `id`. + + // Create a new **Collection**, perhaps to contain a specific type of `model`. + // If a `comparator` is specified, the Collection will maintain + // its models in sort order, as they're added and removed. + var Collection = Backbone.Collection = function(models, options) { + options || (options = {}); + if (options.model) this.model = options.model; + if (options.comparator !== void 0) this.comparator = options.comparator; + this._reset(); + this.initialize.apply(this, arguments); + if (models) this.reset(models, _.extend({silent: true}, options)); + }; + + // Default options for `Collection#set`. + var setOptions = {add: true, remove: true, merge: true}; + var addOptions = {add: true, remove: false}; + + // Define the Collection's inheritable methods. + _.extend(Collection.prototype, Events, { + + // The default model for a collection is just a **Backbone.Model**. + // This should be overridden in most cases. + model: Model, + + // Initialize is an empty function by default. Override it with your own + // initialization logic. + initialize: function(){}, + + // The JSON representation of a Collection is an array of the + // models' attributes. + toJSON: function(options) { + return this.map(function(model){ return model.toJSON(options); }); + }, + + // Proxy `Backbone.sync` by default. + sync: function() { + return Backbone.sync.apply(this, arguments); + }, + + // Add a model, or list of models to the set. + add: function(models, options) { + return this.set(models, _.extend({merge: false}, options, addOptions)); + }, + + // Remove a model, or a list of models from the set. + remove: function(models, options) { + var singular = !_.isArray(models); + models = singular ? [models] : _.clone(models); + options || (options = {}); + var i, l, index, model; + for (i = 0, l = models.length; i < l; i++) { + model = models[i] = this.get(models[i]); + if (!model) continue; + delete this._byId[model.id]; + delete this._byId[model.cid]; + index = this.indexOf(model); + this.models.splice(index, 1); + this.length--; + if (!options.silent) { + options.index = index; + model.trigger('remove', model, this, options); + } + this._removeReference(model, options); + } + return singular ? models[0] : models; + }, + + // Update a collection by `set`-ing a new list of models, adding new ones, + // removing models that are no longer present, and merging models that + // already exist in the collection, as necessary. Similar to **Model#set**, + // the core operation for updating the data contained by the collection. + set: function(models, options) { + options = _.defaults({}, options, setOptions); + if (options.parse) models = this.parse(models, options); + var singular = !_.isArray(models); + models = singular ? (models ? [models] : []) : _.clone(models); + var i, l, id, model, attrs, existing, sort; + var at = options.at; + var targetModel = this.model; + var sortable = this.comparator && (at == null) && options.sort !== false; + var sortAttr = _.isString(this.comparator) ? this.comparator : null; + var toAdd = [], toRemove = [], modelMap = {}; + var add = options.add, merge = options.merge, remove = options.remove; + var order = !sortable && add && remove ? [] : false; + + // Turn bare objects into model references, and prevent invalid models + // from being added. + for (i = 0, l = models.length; i < l; i++) { + attrs = models[i] || {}; + if (attrs instanceof Model) { + id = model = attrs; + } else { + id = attrs[targetModel.prototype.idAttribute || 'id']; + } + + // If a duplicate is found, prevent it from being added and + // optionally merge it into the existing model. + if (existing = this.get(id)) { + if (remove) modelMap[existing.cid] = true; + if (merge) { + attrs = attrs === model ? model.attributes : attrs; + if (options.parse) attrs = existing.parse(attrs, options); + existing.set(attrs, options); + if (sortable && !sort && existing.hasChanged(sortAttr)) sort = true; + } + models[i] = existing; + + // If this is a new, valid model, push it to the `toAdd` list. + } else if (add) { + model = models[i] = this._prepareModel(attrs, options); + if (!model) continue; + toAdd.push(model); + this._addReference(model, options); + } + + // Do not add multiple models with the same `id`. + model = existing || model; + if (order && (model.isNew() || !modelMap[model.id])) order.push(model); + modelMap[model.id] = true; + } + + // Remove nonexistent models if appropriate. + if (remove) { + for (i = 0, l = this.length; i < l; ++i) { + if (!modelMap[(model = this.models[i]).cid]) toRemove.push(model); + } + if (toRemove.length) this.remove(toRemove, options); + } + + // See if sorting is needed, update `length` and splice in new models. + if (toAdd.length || (order && order.length)) { + if (sortable) sort = true; + this.length += toAdd.length; + if (at != null) { + for (i = 0, l = toAdd.length; i < l; i++) { + this.models.splice(at + i, 0, toAdd[i]); + } + } else { + if (order) this.models.length = 0; + var orderedModels = order || toAdd; + for (i = 0, l = orderedModels.length; i < l; i++) { + this.models.push(orderedModels[i]); + } + } + } + + // Silently sort the collection if appropriate. + if (sort) this.sort({silent: true}); + + // Unless silenced, it's time to fire all appropriate add/sort events. + if (!options.silent) { + for (i = 0, l = toAdd.length; i < l; i++) { + (model = toAdd[i]).trigger('add', model, this, options); + } + if (sort || (order && order.length)) this.trigger('sort', this, options); + } + + // Return the added (or merged) model (or models). + return singular ? models[0] : models; + }, + + // When you have more items than you want to add or remove individually, + // you can reset the entire set with a new list of models, without firing + // any granular `add` or `remove` events. Fires `reset` when finished. + // Useful for bulk operations and optimizations. + reset: function(models, options) { + options || (options = {}); + for (var i = 0, l = this.models.length; i < l; i++) { + this._removeReference(this.models[i], options); + } + options.previousModels = this.models; + this._reset(); + models = this.add(models, _.extend({silent: true}, options)); + if (!options.silent) this.trigger('reset', this, options); + return models; + }, + + // Add a model to the end of the collection. + push: function(model, options) { + return this.add(model, _.extend({at: this.length}, options)); + }, + + // Remove a model from the end of the collection. + pop: function(options) { + var model = this.at(this.length - 1); + this.remove(model, options); + return model; + }, + + // Add a model to the beginning of the collection. + unshift: function(model, options) { + return this.add(model, _.extend({at: 0}, options)); + }, + + // Remove a model from the beginning of the collection. + shift: function(options) { + var model = this.at(0); + this.remove(model, options); + return model; + }, + + // Slice out a sub-array of models from the collection. + slice: function() { + return slice.apply(this.models, arguments); + }, + + // Get a model from the set by id. + get: function(obj) { + if (obj == null) return void 0; + return this._byId[obj] || this._byId[obj.id] || this._byId[obj.cid]; + }, + + // Get the model at the given index. + at: function(index) { + return this.models[index]; + }, + + // Return models with matching attributes. Useful for simple cases of + // `filter`. + where: function(attrs, first) { + if (_.isEmpty(attrs)) return first ? void 0 : []; + return this[first ? 'find' : 'filter'](function(model) { + for (var key in attrs) { + if (attrs[key] !== model.get(key)) return false; + } + return true; + }); + }, + + // Return the first model with matching attributes. Useful for simple cases + // of `find`. + findWhere: function(attrs) { + return this.where(attrs, true); + }, + + // Force the collection to re-sort itself. You don't need to call this under + // normal circumstances, as the set will maintain sort order as each item + // is added. + sort: function(options) { + if (!this.comparator) throw new Error('Cannot sort a set without a comparator'); + options || (options = {}); + + // Run sort based on type of `comparator`. + if (_.isString(this.comparator) || this.comparator.length === 1) { + this.models = this.sortBy(this.comparator, this); + } else { + this.models.sort(_.bind(this.comparator, this)); + } + + if (!options.silent) this.trigger('sort', this, options); + return this; + }, + + // Pluck an attribute from each model in the collection. + pluck: function(attr) { + return _.invoke(this.models, 'get', attr); + }, + + // Fetch the default set of models for this collection, resetting the + // collection when they arrive. If `reset: true` is passed, the response + // data will be passed through the `reset` method instead of `set`. + fetch: function(options) { + options = options ? _.clone(options) : {}; + if (options.parse === void 0) options.parse = true; + var success = options.success; + var collection = this; + options.success = function(resp) { + var method = options.reset ? 'reset' : 'set'; + collection[method](resp, options); + if (success) success(collection, resp, options); + collection.trigger('sync', collection, resp, options); + }; + wrapError(this, options); + return this.sync('read', this, options); + }, + + // Create a new instance of a model in this collection. Add the model to the + // collection immediately, unless `wait: true` is passed, in which case we + // wait for the server to agree. + create: function(model, options) { + options = options ? _.clone(options) : {}; + if (!(model = this._prepareModel(model, options))) return false; + if (!options.wait) this.add(model, options); + var collection = this; + var success = options.success; + options.success = function(model, resp) { + if (options.wait) collection.add(model, options); + if (success) success(model, resp, options); + }; + model.save(null, options); + return model; + }, + + // **parse** converts a response into a list of models to be added to the + // collection. The default implementation is just to pass it through. + parse: function(resp, options) { + return resp; + }, + + // Create a new collection with an identical list of models as this one. + clone: function() { + return new this.constructor(this.models); + }, + + // Private method to reset all internal state. Called when the collection + // is first initialized or reset. + _reset: function() { + this.length = 0; + this.models = []; + this._byId = {}; + }, + + // Prepare a hash of attributes (or other model) to be added to this + // collection. + _prepareModel: function(attrs, options) { + if (attrs instanceof Model) return attrs; + options = options ? _.clone(options) : {}; + options.collection = this; + var model = new this.model(attrs, options); + if (!model.validationError) return model; + this.trigger('invalid', this, model.validationError, options); + return false; + }, + + // Internal method to create a model's ties to a collection. + _addReference: function(model, options) { + this._byId[model.cid] = model; + if (model.id != null) this._byId[model.id] = model; + if (!model.collection) model.collection = this; + model.on('all', this._onModelEvent, this); + }, + + // Internal method to sever a model's ties to a collection. + _removeReference: function(model, options) { + if (this === model.collection) delete model.collection; + model.off('all', this._onModelEvent, this); + }, + + // Internal method called every time a model in the set fires an event. + // Sets need to update their indexes when models change ids. All other + // events simply proxy through. "add" and "remove" events that originate + // in other collections are ignored. + _onModelEvent: function(event, model, collection, options) { + if ((event === 'add' || event === 'remove') && collection !== this) return; + if (event === 'destroy') this.remove(model, options); + if (model && event === 'change:' + model.idAttribute) { + delete this._byId[model.previous(model.idAttribute)]; + if (model.id != null) this._byId[model.id] = model; + } + this.trigger.apply(this, arguments); + } + + }); + + // Underscore methods that we want to implement on the Collection. + // 90% of the core usefulness of Backbone Collections is actually implemented + // right here: + var methods = ['forEach', 'each', 'map', 'collect', 'reduce', 'foldl', + 'inject', 'reduceRight', 'foldr', 'find', 'detect', 'filter', 'select', + 'reject', 'every', 'all', 'some', 'any', 'include', 'contains', 'invoke', + 'max', 'min', 'toArray', 'size', 'first', 'head', 'take', 'initial', 'rest', + 'tail', 'drop', 'last', 'without', 'difference', 'indexOf', 'shuffle', + 'lastIndexOf', 'isEmpty', 'chain', 'sample']; + + // Mix in each Underscore method as a proxy to `Collection#models`. + _.each(methods, function(method) { + Collection.prototype[method] = function() { + var args = slice.call(arguments); + args.unshift(this.models); + return _[method].apply(_, args); + }; + }); + + // Underscore methods that take a property name as an argument. + var attributeMethods = ['groupBy', 'countBy', 'sortBy', 'indexBy']; + + // Use attributes instead of properties. + _.each(attributeMethods, function(method) { + Collection.prototype[method] = function(value, context) { + var iterator = _.isFunction(value) ? value : function(model) { + return model.get(value); + }; + return _[method](this.models, iterator, context); + }; + }); + + // Backbone.View + // ------------- + + // Backbone Views are almost more convention than they are actual code. A View + // is simply a JavaScript object that represents a logical chunk of UI in the + // DOM. This might be a single item, an entire list, a sidebar or panel, or + // even the surrounding frame which wraps your whole app. Defining a chunk of + // UI as a **View** allows you to define your DOM events declaratively, without + // having to worry about render order ... and makes it easy for the view to + // react to specific changes in the state of your models. + + // Creating a Backbone.View creates its initial element outside of the DOM, + // if an existing element is not provided... + var View = Backbone.View = function(options) { + this.cid = _.uniqueId('view'); + options || (options = {}); + _.extend(this, _.pick(options, viewOptions)); + this._ensureElement(); + this.initialize.apply(this, arguments); + this.delegateEvents(); + }; + + // Cached regex to split keys for `delegate`. + var delegateEventSplitter = /^(\S+)\s*(.*)$/; + + // List of view options to be merged as properties. + var viewOptions = ['model', 'collection', 'el', 'id', 'attributes', 'className', 'tagName', 'events']; + + // Set up all inheritable **Backbone.View** properties and methods. + _.extend(View.prototype, Events, { + + // The default `tagName` of a View's element is `"div"`. + tagName: 'div', + + // jQuery delegate for element lookup, scoped to DOM elements within the + // current view. This should be preferred to global lookups where possible. + $: function(selector) { + return this.$el.find(selector); + }, + + // Initialize is an empty function by default. Override it with your own + // initialization logic. + initialize: function(){}, + + // **render** is the core function that your view should override, in order + // to populate its element (`this.el`), with the appropriate HTML. The + // convention is for **render** to always return `this`. + render: function() { + return this; + }, + + // Remove this view by taking the element out of the DOM, and removing any + // applicable Backbone.Events listeners. + remove: function() { + this.$el.remove(); + this.stopListening(); + return this; + }, + + // Change the view's element (`this.el` property), including event + // re-delegation. + setElement: function(element, delegate) { + if (this.$el) this.undelegateEvents(); + this.$el = element instanceof Backbone.$ ? element : Backbone.$(element); + this.el = this.$el[0]; + if (delegate !== false) this.delegateEvents(); + return this; + }, + + // Set callbacks, where `this.events` is a hash of + // + // *{"event selector": "callback"}* + // + // { + // 'mousedown .title': 'edit', + // 'click .button': 'save', + // 'click .open': function(e) { ... } + // } + // + // pairs. Callbacks will be bound to the view, with `this` set properly. + // Uses event delegation for efficiency. + // Omitting the selector binds the event to `this.el`. + // This only works for delegate-able events: not `focus`, `blur`, and + // not `change`, `submit`, and `reset` in Internet Explorer. + delegateEvents: function(events) { + if (!(events || (events = _.result(this, 'events')))) return this; + this.undelegateEvents(); + for (var key in events) { + var method = events[key]; + if (!_.isFunction(method)) method = this[events[key]]; + if (!method) continue; + + var match = key.match(delegateEventSplitter); + var eventName = match[1], selector = match[2]; + method = _.bind(method, this); + eventName += '.delegateEvents' + this.cid; + if (selector === '') { + this.$el.on(eventName, method); + } else { + this.$el.on(eventName, selector, method); + } + } + return this; + }, + + // Clears all callbacks previously bound to the view with `delegateEvents`. + // You usually don't need to use this, but may wish to if you have multiple + // Backbone views attached to the same DOM element. + undelegateEvents: function() { + this.$el.off('.delegateEvents' + this.cid); + return this; + }, + + // Ensure that the View has a DOM element to render into. + // If `this.el` is a string, pass it through `$()`, take the first + // matching element, and re-assign it to `el`. Otherwise, create + // an element from the `id`, `className` and `tagName` properties. + _ensureElement: function() { + if (!this.el) { + var attrs = _.extend({}, _.result(this, 'attributes')); + if (this.id) attrs.id = _.result(this, 'id'); + if (this.className) attrs['class'] = _.result(this, 'className'); + var $el = Backbone.$('<' + _.result(this, 'tagName') + '>').attr(attrs); + this.setElement($el, false); + } else { + this.setElement(_.result(this, 'el'), false); + } + } + + }); + + // Backbone.sync + // ------------- + + // Override this function to change the manner in which Backbone persists + // models to the server. You will be passed the type of request, and the + // model in question. By default, makes a RESTful Ajax request + // to the model's `url()`. Some possible customizations could be: + // + // * Use `setTimeout` to batch rapid-fire updates into a single request. + // * Send up the models as XML instead of JSON. + // * Persist models via WebSockets instead of Ajax. + // + // Turn on `Backbone.emulateHTTP` in order to send `PUT` and `DELETE` requests + // as `POST`, with a `_method` parameter containing the true HTTP method, + // as well as all requests with the body as `application/x-www-form-urlencoded` + // instead of `application/json` with the model in a param named `model`. + // Useful when interfacing with server-side languages like **PHP** that make + // it difficult to read the body of `PUT` requests. + Backbone.sync = function(method, model, options) { + var type = methodMap[method]; + + // Default options, unless specified. + _.defaults(options || (options = {}), { + emulateHTTP: Backbone.emulateHTTP, + emulateJSON: Backbone.emulateJSON + }); + + // Default JSON-request options. + var params = {type: type, dataType: 'json'}; + + // Ensure that we have a URL. + if (!options.url) { + params.url = _.result(model, 'url') || urlError(); + } + + // Ensure that we have the appropriate request data. + if (options.data == null && model && (method === 'create' || method === 'update' || method === 'patch')) { + params.contentType = 'application/json'; + params.data = JSON.stringify(options.attrs || model.toJSON(options)); + } + + // For older servers, emulate JSON by encoding the request into an HTML-form. + if (options.emulateJSON) { + params.contentType = 'application/x-www-form-urlencoded'; + params.data = params.data ? {model: params.data} : {}; + } + + // For older servers, emulate HTTP by mimicking the HTTP method with `_method` + // And an `X-HTTP-Method-Override` header. + if (options.emulateHTTP && (type === 'PUT' || type === 'DELETE' || type === 'PATCH')) { + params.type = 'POST'; + if (options.emulateJSON) params.data._method = type; + var beforeSend = options.beforeSend; + options.beforeSend = function(xhr) { + xhr.setRequestHeader('X-HTTP-Method-Override', type); + if (beforeSend) return beforeSend.apply(this, arguments); + }; + } + + // Don't process data on a non-GET request. + if (params.type !== 'GET' && !options.emulateJSON) { + params.processData = false; + } + + // If we're sending a `PATCH` request, and we're in an old Internet Explorer + // that still has ActiveX enabled by default, override jQuery to use that + // for XHR instead. Remove this line when jQuery supports `PATCH` on IE8. + if (params.type === 'PATCH' && noXhrPatch) { + params.xhr = function() { + return new ActiveXObject("Microsoft.XMLHTTP"); + }; + } + + // Make the request, allowing the user to override any Ajax options. + var xhr = options.xhr = Backbone.ajax(_.extend(params, options)); + model.trigger('request', model, xhr, options); + return xhr; + }; + + var noXhrPatch = + typeof window !== 'undefined' && !!window.ActiveXObject && + !(window.XMLHttpRequest && (new XMLHttpRequest).dispatchEvent); + + // Map from CRUD to HTTP for our default `Backbone.sync` implementation. + var methodMap = { + 'create': 'POST', + 'update': 'PUT', + 'patch': 'PATCH', + 'delete': 'DELETE', + 'read': 'GET' + }; + + // Set the default implementation of `Backbone.ajax` to proxy through to `$`. + // Override this if you'd like to use a different library. + Backbone.ajax = function() { + return Backbone.$.ajax.apply(Backbone.$, arguments); + }; + + // Backbone.Router + // --------------- + + // Routers map faux-URLs to actions, and fire events when routes are + // matched. Creating a new one sets its `routes` hash, if not set statically. + var Router = Backbone.Router = function(options) { + options || (options = {}); + if (options.routes) this.routes = options.routes; + this._bindRoutes(); + this.initialize.apply(this, arguments); + }; + + // Cached regular expressions for matching named param parts and splatted + // parts of route strings. + var optionalParam = /\((.*?)\)/g; + var namedParam = /(\(\?)?:\w+/g; + var splatParam = /\*\w+/g; + var escapeRegExp = /[\-{}\[\]+?.,\\\^$|#\s]/g; + + // Set up all inheritable **Backbone.Router** properties and methods. + _.extend(Router.prototype, Events, { + + // Initialize is an empty function by default. Override it with your own + // initialization logic. + initialize: function(){}, + + // Manually bind a single named route to a callback. For example: + // + // this.route('search/:query/p:num', 'search', function(query, num) { + // ... + // }); + // + route: function(route, name, callback) { + if (!_.isRegExp(route)) route = this._routeToRegExp(route); + if (_.isFunction(name)) { + callback = name; + name = ''; + } + if (!callback) callback = this[name]; + var router = this; + Backbone.history.route(route, function(fragment) { + var args = router._extractParameters(route, fragment); + router.execute(callback, args); + router.trigger.apply(router, ['route:' + name].concat(args)); + router.trigger('route', name, args); + Backbone.history.trigger('route', router, name, args); + }); + return this; + }, + + // Execute a route handler with the provided parameters. This is an + // excellent place to do pre-route setup or post-route cleanup. + execute: function(callback, args) { + if (callback) callback.apply(this, args); + }, + + // Simple proxy to `Backbone.history` to save a fragment into the history. + navigate: function(fragment, options) { + Backbone.history.navigate(fragment, options); + return this; + }, + + // Bind all defined routes to `Backbone.history`. We have to reverse the + // order of the routes here to support behavior where the most general + // routes can be defined at the bottom of the route map. + _bindRoutes: function() { + if (!this.routes) return; + this.routes = _.result(this, 'routes'); + var route, routes = _.keys(this.routes); + while ((route = routes.pop()) != null) { + this.route(route, this.routes[route]); + } + }, + + // Convert a route string into a regular expression, suitable for matching + // against the current location hash. + _routeToRegExp: function(route) { + route = route.replace(escapeRegExp, '\\$&') + .replace(optionalParam, '(?:$1)?') + .replace(namedParam, function(match, optional) { + return optional ? match : '([^/?]+)'; + }) + .replace(splatParam, '([^?]*?)'); + return new RegExp('^' + route + '(?:\\?([\\s\\S]*))?$'); + }, + + // Given a route, and a URL fragment that it matches, return the array of + // extracted decoded parameters. Empty or unmatched parameters will be + // treated as `null` to normalize cross-browser behavior. + _extractParameters: function(route, fragment) { + var params = route.exec(fragment).slice(1); + return _.map(params, function(param, i) { + // Don't decode the search params. + if (i === params.length - 1) return param || null; + return param ? decodeURIComponent(param) : null; + }); + } + + }); + + // Backbone.History + // ---------------- + + // Handles cross-browser history management, based on either + // [pushState](http://diveintohtml5.info/history.html) and real URLs, or + // [onhashchange](https://developer.mozilla.org/en-US/docs/DOM/window.onhashchange) + // and URL fragments. If the browser supports neither (old IE, natch), + // falls back to polling. + var History = Backbone.History = function() { + this.handlers = []; + _.bindAll(this, 'checkUrl'); + + // Ensure that `History` can be used outside of the browser. + if (typeof window !== 'undefined') { + this.location = window.location; + this.history = window.history; + } + }; + + // Cached regex for stripping a leading hash/slash and trailing space. + var routeStripper = /^[#\/]|\s+$/g; + + // Cached regex for stripping leading and trailing slashes. + var rootStripper = /^\/+|\/+$/g; + + // Cached regex for detecting MSIE. + var isExplorer = /msie [\w.]+/; + + // Cached regex for removing a trailing slash. + var trailingSlash = /\/$/; + + // Cached regex for stripping urls of hash. + var pathStripper = /#.*$/; + + // Has the history handling already been started? + History.started = false; + + // Set up all inheritable **Backbone.History** properties and methods. + _.extend(History.prototype, Events, { + + // The default interval to poll for hash changes, if necessary, is + // twenty times a second. + interval: 50, + + // Are we at the app root? + atRoot: function() { + return this.location.pathname.replace(/[^\/]$/, '$&/') === this.root; + }, + + // Gets the true hash value. Cannot use location.hash directly due to bug + // in Firefox where location.hash will always be decoded. + getHash: function(window) { + var match = (window || this).location.href.match(/#(.*)$/); + return match ? match[1] : ''; + }, + + // Get the cross-browser normalized URL fragment, either from the URL, + // the hash, or the override. + getFragment: function(fragment, forcePushState) { + if (fragment == null) { + if (this._hasPushState || !this._wantsHashChange || forcePushState) { + fragment = decodeURI(this.location.pathname + this.location.search); + var root = this.root.replace(trailingSlash, ''); + if (!fragment.indexOf(root)) fragment = fragment.slice(root.length); + } else { + fragment = this.getHash(); + } + } + return fragment.replace(routeStripper, ''); + }, + + // Start the hash change handling, returning `true` if the current URL matches + // an existing route, and `false` otherwise. + start: function(options) { + if (History.started) throw new Error("Backbone.history has already been started"); + History.started = true; + + // Figure out the initial configuration. Do we need an iframe? + // Is pushState desired ... is it available? + this.options = _.extend({root: '/'}, this.options, options); + this.root = this.options.root; + this._wantsHashChange = this.options.hashChange !== false; + this._wantsPushState = !!this.options.pushState; + this._hasPushState = !!(this.options.pushState && this.history && this.history.pushState); + var fragment = this.getFragment(); + var docMode = document.documentMode; + var oldIE = (isExplorer.exec(navigator.userAgent.toLowerCase()) && (!docMode || docMode <= 7)); + + // Normalize root to always include a leading and trailing slash. + this.root = ('/' + this.root + '/').replace(rootStripper, '/'); + + if (oldIE && this._wantsHashChange) { + var frame = Backbone.$('",o=new cdb.ui.common.ShareDialog({title:e.map.get("title"),description:e.map.get("description"),model:t.map,code:s,url:e.url,public_map_url:i,share_url:e.share_url,template:n,target:$(".cartodb-share a"),size:$(document).width()>400?"":"small",width:$(document).width()>400?430:216});return o.render()}),cdb.vis.Overlay.register("search",function(e,t){var n=cdb.core.Template.compile(e.template||'
      ',e.templateType||"mustache"),r=new cdb.geo.ui.Search({template:n,model:t.map});return r.render()}),cdb.vis.Overlay.register("tooltip",function(e,t){var n;if(!e.layer){var r=t.getLayers();r.length>1&&(n=r[1]),e.layer=n}if(!e.layer)throw new Error("layer is null");e.layer.setInteraction(!0);var i=new cdb.geo.ui.Tooltip(e);return i}),cdb.vis.Overlay.register("infobox",function(e,t){var n,r=t.getLayers();e.layer||(r.length>1&&(n=r[1]),e.layer=n);if(!e.layer)throw new Error("layer is null");e.layer.setInteraction(!0);var i=new cdb.geo.ui.InfoBox(e);return i})}(),function(){function n(e){for(var n in t)if(e.indexOf(n)!==-1)return e.replace(n,t[n]);return e}function r(e,t){t.infowindow&&t.infowindow.fields&&(t.interactivity?t.interactivity.indexOf("cartodb_id")===-1&&(t.interactivity=t.interactivity+",cartodb_id"):t.interactivity="cartodb_id"),e.https&&(t.tiler_protocol="https",t.tiler_port=443,t.sql_api_protocol="https",t.sql_api_port=443),t.cartodb_logo=e.cartodb_logo==undefined?t.cartodb_logo:e.cartodb_logo}var e=cdb.vis.Layers,t={"https://dnv9my2eseobd.cloudfront.net/":"http://a.tiles.mapbox.com/","https://maps.nlp.nokia.com/":"http://maps.nlp.nokia.com/","https://tile.stamen.com/":"http://tile.stamen.com/","https://{s}.maps.nlp.nokia.com/":"http://{s}.maps.nlp.nokia.com/","https://cartocdn_{s}.global.ssl.fastly.net/":"http://{s}.api.cartocdn.com/"};e.register("tilejson",function(e,t){var r=t.tiles[0];return r=e.https?r:n(r),new cdb.geo.TileLayer({urlTemplate:r})}),e.register("tiled",function(e,t){var r=t.urlTemplate;return r=e.https?r:n(r),t.urlTemplate=r,new cdb.geo.TileLayer(t)}),e.register("wms",function(e,t){return new cdb.geo.WMSLayer(t)}),e.register("gmapsbase",function(e,t){return new cdb.geo.GMapsBaseLayer(t)}),e.register("plain",function(e,t){return new cdb.geo.PlainLayer(t)}),e.register("background",function(e,t){return new cdb.geo.PlainLayer(t)});var i=function(e,t){return r(e,t),t.sublayers?(t.type="layergroup",new cdb.geo.CartoDBGroupLayer(t)):new cdb.geo.CartoDBLayer(t)};e.register("cartodb",i),e.register("carto",i),e.register("layergroup",function(e,t){return r(e,t),new cdb.geo.CartoDBGroupLayer(t)}),e.register("namedmap",function(e,t){return r(e,t),new cdb.geo.CartoDBNamedMapLayer(t)}),e.register("torque",function(e,t){return e.https&&t.sql_api_domain&&t.sql_api_domain.indexOf("cartodb.com")!==-1&&(t.sql_api_protocol="https",t.sql_api_port=443,t.tiler_protocol="https",t.tiler_port=443),t.cartodb_logo=e.cartodb_logo==undefined?t.cartodb_logo:e.cartodb_logo,new cdb.geo.TorqueLayer(t)})}(),function(){function e(){}function n(e){var t=e.host||"cartodb.com",n=e.protocol||"https";return n+"://"+e.user+"."+t+"/api/v1/viz/"+e.table+"/viz.json"}function r(e,t){var r=null;if(e.layers!==undefined||(e.kind||e.type)!==undefined){_.defer(function(){t(e)});return}e.table!==undefined&&e.user!==undefined?r=n(e):e.indexOf&&e.indexOf("http")===0&&(r=e),r?cdb.vis.Loader.get(r,t):_.defer(function(){t(null)})}_.extend(e.prototype,Backbone.Events,{done:function(e){return this.bind("done",e)},error:function(e){return this.bind("error",e)}}),cdb._Promise=e;var t={};cartodb.createLayer=function(t,n,i,s){var o=new e,u,a;i=i||{};if(t===undefined)throw new TypeError("map should be provided");if(n===undefined)throw new TypeError("layer should be provided");var f=arguments,l=f[f.length-1];return _.isFunction(l)&&(s=l),o.addTo=function(e,t){return o.on("done",function(){a.addLayerToMap(u,e,t)}),o},r(n,function(e){function c(){u=f.createLayer(n,{no_base_layer:!0});if(!u)return o.trigger("error","layer not supported"),o;i.infowindow&&f.addInfowindow(u),i.tooltip&&f.addTooltip(u),i.legends&&f.addLegends([n]),i.time_slider&&u.model.get("type")==="torque"&&f.addTimeSlider(u),s&&s(u),o.trigger("done",u)}var n;if(!e){o.trigger("error");return}if(e.layers){e.layers.length<2&&o.trigger("error","visualization file does not contain layer info");var r=i.layerIndex===undefined?1:i.layerIndex;if(e.layers.length<=r){o.trigger("error","layerIndex out of bounds");return}n=e.layers[r]}else n=e;if(!n){o.trigger("error");return}i&&!_.isFunction(i)&&(n.options=n.options||{},_.extend(n.options,i)),i=_.defaults(i,{infowindow:!0,https:!1,legends:!0,time_slider:!0,tooltip:!0});if(typeof t.overlayMapTypes!="undefined")a=cdb.geo.GoogleMapsMapView;else{if(!(t instanceof L.Map||window.L&&t instanceof window.L.Map))return o.trigger("error","cartodb.js can't guess the map type"),o;a=cdb.geo.LeafletMapView}var f=t.viz;if(!f){var l=new a({map_object:t,map:new cdb.geo.Map});t.viz=f=new cdb.vis.Vis({mapView:l}),f.updated_at=e.updated_at,f.https=i.https}f.checkModules([n])?c():f.loadModules([n],function(){c()})}),o}}(),function(){function t(e){if(cartodb===this||window===this)return new t(e);if(!e.user)throw new Error("user should be provided");var n=new String(window.location.protocol);n=n.slice(0,n.length-1),n=="file"&&(n="https"),this.ajax=e.ajax||(typeof jQuery!="undefined"?jQuery.ajax:reqwest);if(!this.ajax)throw new Error("jQuery or reqwest should be loaded");this.options=_.defaults(e,{version:"v2",protocol:n,jsonp:typeof jQuery!="undefined"?!jQuery.support.cors:!1})}var e=this;e.cartodb=e.cartodb||{},t.prototype._host=function(){var e=this.options;if(e&&e.completeDomain)return e.completeDomain+"/api/"+e.version+"/sql";var t=e.host||"cartodb.com",n=e.protocol||"https";return n+"://"+e.user+"."+t+"/api/"+e.version+"/sql"},t.prototype.execute=function(e,t,n,r){var i=new cartodb._Promise;if(!e)throw new TypeError("sql should not be null");var s=arguments,o=s[s.length-1];_.isFunction(o)&&(r=o),n=_.defaults(n||{},this.options);var u={type:"get",dataType:"json",crossDomain:!0};n.jsonp&&(delete u.crossDomain,u.dataType="jsonp"),n.cache&&(u.cache=n.cache);var a="156543.03515625",f="ST_MakeEnvelope(-20037508.5,-20037508.5,20037508.5,20037508.5,3857)";e=e.replace("!bbox!",f).replace("!pixel_width!",a).replace("!pixel_height!",a);var l=Mustache.render(e,t),c="q="+encodeURIComponent(l),h=["format","dp","api_key"];n.extra_params&&(h=h.concat(n.extra_params));for(var p in h){var d=h[p],v=n[d];v&&(c+="&"+d+"="+v)}var m=n.type?n.type=="get":u.type=="get";u.url=this._host(),m?u.url+="?"+c:u.data=c;var g=n.success,y=n.error;return g&&delete n.success,y&&delete y.success,u.error=function(e){var t=e.responseText||e.response,n=t&&JSON.parse(t);i.trigger("error",n&&n.error,e),y&&y(e)},u.success=function(e,t,n){t==undefined&&(t=e.status,n=e,e=JSON.parse(e.response)),i.trigger("done",e,t,n),g&&g(e,t,n),r&&r(e)},delete n.jsonp,this.ajax(_.extend(u,n)),i},t.prototype.getBounds=function(e,t,n,r){var i=new cartodb._Promise,s=arguments,o=s[s.length-1];_.isFunction(o)&&(r=o);var u="SELECT ST_XMin(ST_Extent(the_geom)) as minx, ST_YMin(ST_Extent(the_geom)) as miny, ST_XMax(ST_Extent(the_geom)) as maxx, ST_YMax(ST_Extent(the_geom)) as maxy from ({{{ sql }}}) as subq";return e=Mustache.render(e,t),this.execute(u,{sql:e},n).done(function(e){if(e.rows&&e.rows.length>0&&e.rows[0].maxx!=null){var t=e.rows[0],n=-85.0511,s=85.0511,o=-179,u=179,a=function(e,t,n){return en?n:e},f=a(t.maxx,o,u),l=a(t.minx,o,u),c=a(t.maxy,n,s),h=a(t.miny,n,s),p=[[c,f],[h,l]];i.trigger("done",p),r&&r(p)}}).error(function(e){i.trigger("error",e)}),i},t.prototype.table=function(e){function a(){a.fetch.apply(a,arguments)}var t=e,n,r=[],i,s,o,u=this;return a.fetch=function(e){e=e||{};var t=arguments,n=t[t.length-1];_.isFunction(n)&&(callback=n,t.length===1&&(e={})),u.execute(a.sql(),e,callback)},a.sql=function(){var e="select";return r.length?e+=" "+r.join(",")+" ":e+=" * ",e+="from "+t,n&&(e+=" where "+n),i&&(e+=" limit "+i),s&&(e+=" order by "+s),o&&(e+=" "+o),e},a.filter=function(e){return n=e,a},a.order_by=function(e){return s=e,a},a.asc=function(){return o="asc",a},a.desc=function(){return o="desc",a},a.columns=function(e){return r=e,a},a.limit=function(e){return i=e,a},a},e.cartodb.SQL=t}(),function(){cartodb.createVis=function(e,t,n,r){if(!e)throw new TypeError("a DOM element should be provided");var i=arguments,s=i[i.length-1];_.isFunction(s)&&(r=s),e=typeof e=="string"?document.getElementById(e):e;var o=new cartodb.vis.Vis({el:e});return t&&(o.load(t,n),r&&o.done(r)),o}}(),cdb.$=$,cdb.L=L,cdb.Mustache=Mustache,cdb.Backbone=Backbone,cdb._=_}();for(var i in __prev)__prev[i]&&(window[i]=__prev[i])})(); \ No newline at end of file diff --git a/vendor/assets/javascripts/chai.js b/vendor/assets/javascripts/chai.js new file mode 100644 index 0000000000..a75da85961 --- /dev/null +++ b/vendor/assets/javascripts/chai.js @@ -0,0 +1,4782 @@ +;(function(){ + +/** + * Require the given path. + * + * @param {String} path + * @return {Object} exports + * @api public + */ + +function require(path, parent, orig) { + var resolved = require.resolve(path); + + // lookup failed + if (null == resolved) { + orig = orig || path; + parent = parent || 'root'; + var err = new Error('Failed to require "' + orig + '" from "' + parent + '"'); + err.path = orig; + err.parent = parent; + err.require = true; + throw err; + } + + var module = require.modules[resolved]; + + // perform real require() + // by invoking the module's + // registered function + if (!module._resolving && !module.exports) { + var mod = {}; + mod.exports = {}; + mod.client = mod.component = true; + module._resolving = true; + module.call(this, mod.exports, require.relative(resolved), mod); + delete module._resolving; + module.exports = mod.exports; + } + + return module.exports; +} + +/** + * Registered modules. + */ + +require.modules = {}; + +/** + * Registered aliases. + */ + +require.aliases = {}; + +/** + * Resolve `path`. + * + * Lookup: + * + * - PATH/index.js + * - PATH.js + * - PATH + * + * @param {String} path + * @return {String} path or null + * @api private + */ + +require.resolve = function(path) { + if (path.charAt(0) === '/') path = path.slice(1); + + var paths = [ + path, + path + '.js', + path + '.json', + path + '/index.js', + path + '/index.json' + ]; + + for (var i = 0; i < paths.length; i++) { + var path = paths[i]; + if (require.modules.hasOwnProperty(path)) return path; + if (require.aliases.hasOwnProperty(path)) return require.aliases[path]; + } +}; + +/** + * Normalize `path` relative to the current path. + * + * @param {String} curr + * @param {String} path + * @return {String} + * @api private + */ + +require.normalize = function(curr, path) { + var segs = []; + + if ('.' != path.charAt(0)) return path; + + curr = curr.split('/'); + path = path.split('/'); + + for (var i = 0; i < path.length; ++i) { + if ('..' == path[i]) { + curr.pop(); + } else if ('.' != path[i] && '' != path[i]) { + segs.push(path[i]); + } + } + + return curr.concat(segs).join('/'); +}; + +/** + * Register module at `path` with callback `definition`. + * + * @param {String} path + * @param {Function} definition + * @api private + */ + +require.register = function(path, definition) { + require.modules[path] = definition; +}; + +/** + * Alias a module definition. + * + * @param {String} from + * @param {String} to + * @api private + */ + +require.alias = function(from, to) { + if (!require.modules.hasOwnProperty(from)) { + throw new Error('Failed to alias "' + from + '", it does not exist'); + } + require.aliases[to] = from; +}; + +/** + * Return a require function relative to the `parent` path. + * + * @param {String} parent + * @return {Function} + * @api private + */ + +require.relative = function(parent) { + var p = require.normalize(parent, '..'); + + /** + * lastIndexOf helper. + */ + + function lastIndexOf(arr, obj) { + var i = arr.length; + while (i--) { + if (arr[i] === obj) return i; + } + return -1; + } + + /** + * The relative require() itself. + */ + + function localRequire(path) { + var resolved = localRequire.resolve(path); + return require(resolved, parent, path); + } + + /** + * Resolve relative to the parent. + */ + + localRequire.resolve = function(path) { + var c = path.charAt(0); + if ('/' == c) return path.slice(1); + if ('.' == c) return require.normalize(p, path); + + // resolve deps by returning + // the dep in the nearest "deps" + // directory + var segs = parent.split('/'); + var i = lastIndexOf(segs, 'deps') + 1; + if (!i) i = 0; + path = segs.slice(0, i + 1).join('/') + '/deps/' + path; + return path; + }; + + /** + * Check if module is defined at `path`. + */ + + localRequire.exists = function(path) { + return require.modules.hasOwnProperty(localRequire.resolve(path)); + }; + + return localRequire; +}; +require.register("chaijs-assertion-error/index.js", function(exports, require, module){ +/*! + * assertion-error + * Copyright(c) 2013 Jake Luer + * MIT Licensed + */ + +/*! + * Return a function that will copy properties from + * one object to another excluding any originally + * listed. Returned function will create a new `{}`. + * + * @param {String} excluded properties ... + * @return {Function} + */ + +function exclude () { + var excludes = [].slice.call(arguments); + + function excludeProps (res, obj) { + Object.keys(obj).forEach(function (key) { + if (!~excludes.indexOf(key)) res[key] = obj[key]; + }); + } + + return function extendExclude () { + var args = [].slice.call(arguments) + , i = 0 + , res = {}; + + for (; i < args.length; i++) { + excludeProps(res, args[i]); + } + + return res; + }; +}; + +/*! + * Primary Exports + */ + +module.exports = AssertionError; + +/** + * ### AssertionError + * + * An extension of the JavaScript `Error` constructor for + * assertion and validation scenarios. + * + * @param {String} message + * @param {Object} properties to include (optional) + * @param {callee} start stack function (optional) + */ + +function AssertionError (message, _props, ssf) { + var extend = exclude('name', 'message', 'stack', 'constructor', 'toJSON') + , props = extend(_props || {}); + + // default values + this.message = message || 'Unspecified AssertionError'; + this.showDiff = false; + + // copy from properties + for (var key in props) { + this[key] = props[key]; + } + + // capture stack trace + ssf = ssf || arguments.callee; + if (ssf && Error.captureStackTrace) { + Error.captureStackTrace(this, ssf); + } +} + +/*! + * Inherit from Error.prototype + */ + +AssertionError.prototype = Object.create(Error.prototype); + +/*! + * Statically set name + */ + +AssertionError.prototype.name = 'AssertionError'; + +/*! + * Ensure correct constructor + */ + +AssertionError.prototype.constructor = AssertionError; + +/** + * Allow errors to be converted to JSON for static transfer. + * + * @param {Boolean} include stack (default: `true`) + * @return {Object} object that can be `JSON.stringify` + */ + +AssertionError.prototype.toJSON = function (stack) { + var extend = exclude('constructor', 'toJSON', 'stack') + , props = extend({ name: this.name }, this); + + // include stack if exists and not turned off + if (false !== stack && this.stack) { + props.stack = this.stack; + } + + return props; +}; + +}); +require.register("chaijs-type-detect/lib/type.js", function(exports, require, module){ +/*! + * type-detect + * Copyright(c) 2013 jake luer + * MIT Licensed + */ + +/*! + * Primary Exports + */ + +var exports = module.exports = getType; + +/*! + * Detectable javascript natives + */ + +var natives = { + '[object Array]': 'array' + , '[object RegExp]': 'regexp' + , '[object Function]': 'function' + , '[object Arguments]': 'arguments' + , '[object Date]': 'date' +}; + +/** + * ### typeOf (obj) + * + * Use several different techniques to determine + * the type of object being tested. + * + * + * @param {Mixed} object + * @return {String} object type + * @api public + */ + +function getType (obj) { + var str = Object.prototype.toString.call(obj); + if (natives[str]) return natives[str]; + if (obj === null) return 'null'; + if (obj === undefined) return 'undefined'; + if (obj === Object(obj)) return 'object'; + return typeof obj; +} + +exports.Library = Library; + +/** + * ### Library + * + * Create a repository for custom type detection. + * + * ```js + * var lib = new type.Library; + * ``` + * + */ + +function Library () { + this.tests = {}; +} + +/** + * #### .of (obj) + * + * Expose replacement `typeof` detection to the library. + * + * ```js + * if ('string' === lib.of('hello world')) { + * // ... + * } + * ``` + * + * @param {Mixed} object to test + * @return {String} type + */ + +Library.prototype.of = getType; + +/** + * #### .define (type, test) + * + * Add a test to for the `.test()` assertion. + * + * Can be defined as a regular expression: + * + * ```js + * lib.define('int', /^[0-9]+$/); + * ``` + * + * ... or as a function: + * + * ```js + * lib.define('bln', function (obj) { + * if ('boolean' === lib.of(obj)) return true; + * var blns = [ 'yes', 'no', 'true', 'false', 1, 0 ]; + * if ('string' === lib.of(obj)) obj = obj.toLowerCase(); + * return !! ~blns.indexOf(obj); + * }); + * ``` + * + * @param {String} type + * @param {RegExp|Function} test + * @api public + */ + +Library.prototype.define = function (type, test) { + if (arguments.length === 1) return this.tests[type]; + this.tests[type] = test; + return this; +}; + +/** + * #### .test (obj, test) + * + * Assert that an object is of type. Will first + * check natives, and if that does not pass it will + * use the user defined custom tests. + * + * ```js + * assert(lib.test('1', 'int')); + * assert(lib.test('yes', 'bln')); + * ``` + * + * @param {Mixed} object + * @param {String} type + * @return {Boolean} result + * @api public + */ + +Library.prototype.test = function (obj, type) { + if (type === getType(obj)) return true; + var test = this.tests[type]; + + if (test && 'regexp' === getType(test)) { + return test.test(obj); + } else if (test && 'function' === getType(test)) { + return test(obj); + } else { + throw new ReferenceError('Type test "' + type + '" not defined or invalid.'); + } +}; + +}); +require.register("chaijs-deep-eql/lib/eql.js", function(exports, require, module){ +/*! + * deep-eql + * Copyright(c) 2013 Jake Luer + * MIT Licensed + */ + +/*! + * Module dependencies + */ + +var type = require('type-detect'); + +/*! + * Buffer.isBuffer browser shim + */ + +var Buffer; +try { Buffer = require('buffer').Buffer; } +catch(ex) { + Buffer = {}; + Buffer.isBuffer = function() { return false; } +} + +/*! + * Primary Export + */ + +module.exports = deepEqual; + +/** + * Assert super-strict (egal) equality between + * two objects of any type. + * + * @param {Mixed} a + * @param {Mixed} b + * @param {Array} memoised (optional) + * @return {Boolean} equal match + */ + +function deepEqual(a, b, m) { + if (sameValue(a, b)) { + return true; + } else if ('date' === type(a)) { + return dateEqual(a, b); + } else if ('regexp' === type(a)) { + return regexpEqual(a, b); + } else if (Buffer.isBuffer(a)) { + return bufferEqual(a, b); + } else if ('arguments' === type(a)) { + return argumentsEqual(a, b, m); + } else if (!typeEqual(a, b)) { + return false; + } else if (('object' !== type(a) && 'object' !== type(b)) + && ('array' !== type(a) && 'array' !== type(b))) { + return sameValue(a, b); + } else { + return objectEqual(a, b, m); + } +} + +/*! + * Strict (egal) equality test. Ensures that NaN always + * equals NaN and `-0` does not equal `+0`. + * + * @param {Mixed} a + * @param {Mixed} b + * @return {Boolean} equal match + */ + +function sameValue(a, b) { + if (a === b) return a !== 0 || 1 / a === 1 / b; + return a !== a && b !== b; +} + +/*! + * Compare the types of two given objects and + * return if they are equal. Note that an Array + * has a type of `array` (not `object`) and arguments + * have a type of `arguments` (not `array`/`object`). + * + * @param {Mixed} a + * @param {Mixed} b + * @return {Boolean} result + */ + +function typeEqual(a, b) { + return type(a) === type(b); +} + +/*! + * Compare two Date objects by asserting that + * the time values are equal using `saveValue`. + * + * @param {Date} a + * @param {Date} b + * @return {Boolean} result + */ + +function dateEqual(a, b) { + if ('date' !== type(b)) return false; + return sameValue(a.getTime(), b.getTime()); +} + +/*! + * Compare two regular expressions by converting them + * to string and checking for `sameValue`. + * + * @param {RegExp} a + * @param {RegExp} b + * @return {Boolean} result + */ + +function regexpEqual(a, b) { + if ('regexp' !== type(b)) return false; + return sameValue(a.toString(), b.toString()); +} + +/*! + * Assert deep equality of two `arguments` objects. + * Unfortunately, these must be sliced to arrays + * prior to test to ensure no bad behavior. + * + * @param {Arguments} a + * @param {Arguments} b + * @param {Array} memoize (optional) + * @return {Boolean} result + */ + +function argumentsEqual(a, b, m) { + if ('arguments' !== type(b)) return false; + a = [].slice.call(a); + b = [].slice.call(b); + return deepEqual(a, b, m); +} + +/*! + * Get enumerable properties of a given object. + * + * @param {Object} a + * @return {Array} property names + */ + +function enumerable(a) { + var res = []; + for (var key in a) res.push(key); + return res; +} + +/*! + * Simple equality for flat iterable objects + * such as Arrays or Node.js buffers. + * + * @param {Iterable} a + * @param {Iterable} b + * @return {Boolean} result + */ + +function iterableEqual(a, b) { + if (a.length !== b.length) return false; + + var i = 0; + var match = true; + + for (; i < a.length; i++) { + if (a[i] !== b[i]) { + match = false; + break; + } + } + + return match; +} + +/*! + * Extension to `iterableEqual` specifically + * for Node.js Buffers. + * + * @param {Buffer} a + * @param {Mixed} b + * @return {Boolean} result + */ + +function bufferEqual(a, b) { + if (!Buffer.isBuffer(b)) return false; + return iterableEqual(a, b); +} + +/*! + * Block for `objectEqual` ensuring non-existing + * values don't get in. + * + * @param {Mixed} object + * @return {Boolean} result + */ + +function isValue(a) { + return a !== null && a !== undefined; +} + +/*! + * Recursively check the equality of two objects. + * Once basic sameness has been established it will + * defer to `deepEqual` for each enumerable key + * in the object. + * + * @param {Mixed} a + * @param {Mixed} b + * @return {Boolean} result + */ + +function objectEqual(a, b, m) { + if (!isValue(a) || !isValue(b)) { + return false; + } + + if (a.prototype !== b.prototype) { + return false; + } + + var i; + if (m) { + for (i = 0; i < m.length; i++) { + if ((m[i][0] === a && m[i][1] === b) + || (m[i][0] === b && m[i][1] === a)) { + return true; + } + } + } else { + m = []; + } + + try { + var ka = enumerable(a); + var kb = enumerable(b); + } catch (ex) { + return false; + } + + ka.sort(); + kb.sort(); + + if (!iterableEqual(ka, kb)) { + return false; + } + + m.push([ a, b ]); + + var key; + for (i = ka.length - 1; i >= 0; i--) { + key = ka[i]; + if (!deepEqual(a[key], b[key], m)) { + return false; + } + } + + return true; +} + +}); +require.register("chai/index.js", function(exports, require, module){ +module.exports = require('./lib/chai'); + +}); +require.register("chai/lib/chai.js", function(exports, require, module){ +/*! + * chai + * Copyright(c) 2011-2014 Jake Luer + * MIT Licensed + */ + +var used = [] + , exports = module.exports = {}; + +/*! + * Chai version + */ + +exports.version = '1.9.1'; + +/*! + * Assertion Error + */ + +exports.AssertionError = require('assertion-error'); + +/*! + * Utils for plugins (not exported) + */ + +var util = require('./chai/utils'); + +/** + * # .use(function) + * + * Provides a way to extend the internals of Chai + * + * @param {Function} + * @returns {this} for chaining + * @api public + */ + +exports.use = function (fn) { + if (!~used.indexOf(fn)) { + fn(this, util); + used.push(fn); + } + + return this; +}; + +/*! + * Configuration + */ + +var config = require('./chai/config'); +exports.config = config; + +/*! + * Primary `Assertion` prototype + */ + +var assertion = require('./chai/assertion'); +exports.use(assertion); + +/*! + * Core Assertions + */ + +var core = require('./chai/core/assertions'); +exports.use(core); + +/*! + * Expect interface + */ + +var expect = require('./chai/interface/expect'); +exports.use(expect); + +/*! + * Should interface + */ + +var should = require('./chai/interface/should'); +exports.use(should); + +/*! + * Assert interface + */ + +var assert = require('./chai/interface/assert'); +exports.use(assert); + +}); +require.register("chai/lib/chai/assertion.js", function(exports, require, module){ +/*! + * chai + * http://chaijs.com + * Copyright(c) 2011-2014 Jake Luer + * MIT Licensed + */ + +var config = require('./config'); + +module.exports = function (_chai, util) { + /*! + * Module dependencies. + */ + + var AssertionError = _chai.AssertionError + , flag = util.flag; + + /*! + * Module export. + */ + + _chai.Assertion = Assertion; + + /*! + * Assertion Constructor + * + * Creates object for chaining. + * + * @api private + */ + + function Assertion (obj, msg, stack) { + flag(this, 'ssfi', stack || arguments.callee); + flag(this, 'object', obj); + flag(this, 'message', msg); + } + + Object.defineProperty(Assertion, 'includeStack', { + get: function() { + console.warn('Assertion.includeStack is deprecated, use chai.config.includeStack instead.'); + return config.includeStack; + }, + set: function(value) { + console.warn('Assertion.includeStack is deprecated, use chai.config.includeStack instead.'); + config.includeStack = value; + } + }); + + Object.defineProperty(Assertion, 'showDiff', { + get: function() { + console.warn('Assertion.showDiff is deprecated, use chai.config.showDiff instead.'); + return config.showDiff; + }, + set: function(value) { + console.warn('Assertion.showDiff is deprecated, use chai.config.showDiff instead.'); + config.showDiff = value; + } + }); + + Assertion.addProperty = function (name, fn) { + util.addProperty(this.prototype, name, fn); + }; + + Assertion.addMethod = function (name, fn) { + util.addMethod(this.prototype, name, fn); + }; + + Assertion.addChainableMethod = function (name, fn, chainingBehavior) { + util.addChainableMethod(this.prototype, name, fn, chainingBehavior); + }; + + Assertion.overwriteProperty = function (name, fn) { + util.overwriteProperty(this.prototype, name, fn); + }; + + Assertion.overwriteMethod = function (name, fn) { + util.overwriteMethod(this.prototype, name, fn); + }; + + Assertion.overwriteChainableMethod = function (name, fn, chainingBehavior) { + util.overwriteChainableMethod(this.prototype, name, fn, chainingBehavior); + }; + + /*! + * ### .assert(expression, message, negateMessage, expected, actual) + * + * Executes an expression and check expectations. Throws AssertionError for reporting if test doesn't pass. + * + * @name assert + * @param {Philosophical} expression to be tested + * @param {String} message to display if fails + * @param {String} negatedMessage to display if negated expression fails + * @param {Mixed} expected value (remember to check for negation) + * @param {Mixed} actual (optional) will default to `this.obj` + * @api private + */ + + Assertion.prototype.assert = function (expr, msg, negateMsg, expected, _actual, showDiff) { + var ok = util.test(this, arguments); + if (true !== showDiff) showDiff = false; + if (true !== config.showDiff) showDiff = false; + + if (!ok) { + var msg = util.getMessage(this, arguments) + , actual = util.getActual(this, arguments); + throw new AssertionError(msg, { + actual: actual + , expected: expected + , showDiff: showDiff + }, (config.includeStack) ? this.assert : flag(this, 'ssfi')); + } + }; + + /*! + * ### ._obj + * + * Quick reference to stored `actual` value for plugin developers. + * + * @api private + */ + + Object.defineProperty(Assertion.prototype, '_obj', + { get: function () { + return flag(this, 'object'); + } + , set: function (val) { + flag(this, 'object', val); + } + }); +}; + +}); +require.register("chai/lib/chai/config.js", function(exports, require, module){ +module.exports = { + + /** + * ### config.includeStack + * + * User configurable property, influences whether stack trace + * is included in Assertion error message. Default of false + * suppresses stack trace in the error message. + * + * chai.config.includeStack = true; // enable stack on error + * + * @param {Boolean} + * @api public + */ + + includeStack: false, + + /** + * ### config.showDiff + * + * User configurable property, influences whether or not + * the `showDiff` flag should be included in the thrown + * AssertionErrors. `false` will always be `false`; `true` + * will be true when the assertion has requested a diff + * be shown. + * + * @param {Boolean} + * @api public + */ + + showDiff: true, + + /** + * ### config.truncateThreshold + * + * User configurable property, sets length threshold for actual and + * expected values in assertion errors. If this threshold is exceeded, + * the value is truncated. + * + * Set it to zero if you want to disable truncating altogether. + * + * chai.config.truncateThreshold = 0; // disable truncating + * + * @param {Number} + * @api public + */ + + truncateThreshold: 40 + +}; + +}); +require.register("chai/lib/chai/core/assertions.js", function(exports, require, module){ +/*! + * chai + * http://chaijs.com + * Copyright(c) 2011-2014 Jake Luer + * MIT Licensed + */ + +module.exports = function (chai, _) { + var Assertion = chai.Assertion + , toString = Object.prototype.toString + , flag = _.flag; + + /** + * ### Language Chains + * + * The following are provided as chainable getters to + * improve the readability of your assertions. They + * do not provide testing capabilities unless they + * have been overwritten by a plugin. + * + * **Chains** + * + * - to + * - be + * - been + * - is + * - that + * - and + * - has + * - have + * - with + * - at + * - of + * - same + * + * @name language chains + * @api public + */ + + [ 'to', 'be', 'been' + , 'is', 'and', 'has', 'have' + , 'with', 'that', 'at' + , 'of', 'same' ].forEach(function (chain) { + Assertion.addProperty(chain, function () { + return this; + }); + }); + + /** + * ### .not + * + * Negates any of assertions following in the chain. + * + * expect(foo).to.not.equal('bar'); + * expect(goodFn).to.not.throw(Error); + * expect({ foo: 'baz' }).to.have.property('foo') + * .and.not.equal('bar'); + * + * @name not + * @api public + */ + + Assertion.addProperty('not', function () { + flag(this, 'negate', true); + }); + + /** + * ### .deep + * + * Sets the `deep` flag, later used by the `equal` and + * `property` assertions. + * + * expect(foo).to.deep.equal({ bar: 'baz' }); + * expect({ foo: { bar: { baz: 'quux' } } }) + * .to.have.deep.property('foo.bar.baz', 'quux'); + * + * @name deep + * @api public + */ + + Assertion.addProperty('deep', function () { + flag(this, 'deep', true); + }); + + /** + * ### .a(type) + * + * The `a` and `an` assertions are aliases that can be + * used either as language chains or to assert a value's + * type. + * + * // typeof + * expect('test').to.be.a('string'); + * expect({ foo: 'bar' }).to.be.an('object'); + * expect(null).to.be.a('null'); + * expect(undefined).to.be.an('undefined'); + * + * // language chain + * expect(foo).to.be.an.instanceof(Foo); + * + * @name a + * @alias an + * @param {String} type + * @param {String} message _optional_ + * @api public + */ + + function an (type, msg) { + if (msg) flag(this, 'message', msg); + type = type.toLowerCase(); + var obj = flag(this, 'object') + , article = ~[ 'a', 'e', 'i', 'o', 'u' ].indexOf(type.charAt(0)) ? 'an ' : 'a '; + + this.assert( + type === _.type(obj) + , 'expected #{this} to be ' + article + type + , 'expected #{this} not to be ' + article + type + ); + } + + Assertion.addChainableMethod('an', an); + Assertion.addChainableMethod('a', an); + + /** + * ### .include(value) + * + * The `include` and `contain` assertions can be used as either property + * based language chains or as methods to assert the inclusion of an object + * in an array or a substring in a string. When used as language chains, + * they toggle the `contain` flag for the `keys` assertion. + * + * expect([1,2,3]).to.include(2); + * expect('foobar').to.contain('foo'); + * expect({ foo: 'bar', hello: 'universe' }).to.include.keys('foo'); + * + * @name include + * @alias contain + * @param {Object|String|Number} obj + * @param {String} message _optional_ + * @api public + */ + + function includeChainingBehavior () { + flag(this, 'contains', true); + } + + function include (val, msg) { + if (msg) flag(this, 'message', msg); + var obj = flag(this, 'object'); + var expected = false; + if (_.type(obj) === 'array' && _.type(val) === 'object') { + for (var i in obj) { + if (_.eql(obj[i], val)) { + expected = true; + break; + } + } + } else if (_.type(val) === 'object') { + if (!flag(this, 'negate')) { + for (var k in val) new Assertion(obj).property(k, val[k]); + return; + } + var subset = {} + for (var k in val) subset[k] = obj[k] + expected = _.eql(subset, val); + } else { + expected = obj && ~obj.indexOf(val) + } + this.assert( + expected + , 'expected #{this} to include ' + _.inspect(val) + , 'expected #{this} to not include ' + _.inspect(val)); + } + + Assertion.addChainableMethod('include', include, includeChainingBehavior); + Assertion.addChainableMethod('contain', include, includeChainingBehavior); + + /** + * ### .ok + * + * Asserts that the target is truthy. + * + * expect('everthing').to.be.ok; + * expect(1).to.be.ok; + * expect(false).to.not.be.ok; + * expect(undefined).to.not.be.ok; + * expect(null).to.not.be.ok; + * + * @name ok + * @api public + */ + + Assertion.addProperty('ok', function () { + this.assert( + flag(this, 'object') + , 'expected #{this} to be truthy' + , 'expected #{this} to be falsy'); + }); + + /** + * ### .true + * + * Asserts that the target is `true`. + * + * expect(true).to.be.true; + * expect(1).to.not.be.true; + * + * @name true + * @api public + */ + + Assertion.addProperty('true', function () { + this.assert( + true === flag(this, 'object') + , 'expected #{this} to be true' + , 'expected #{this} to be false' + , this.negate ? false : true + ); + }); + + /** + * ### .false + * + * Asserts that the target is `false`. + * + * expect(false).to.be.false; + * expect(0).to.not.be.false; + * + * @name false + * @api public + */ + + Assertion.addProperty('false', function () { + this.assert( + false === flag(this, 'object') + , 'expected #{this} to be false' + , 'expected #{this} to be true' + , this.negate ? true : false + ); + }); + + /** + * ### .null + * + * Asserts that the target is `null`. + * + * expect(null).to.be.null; + * expect(undefined).not.to.be.null; + * + * @name null + * @api public + */ + + Assertion.addProperty('null', function () { + this.assert( + null === flag(this, 'object') + , 'expected #{this} to be null' + , 'expected #{this} not to be null' + ); + }); + + /** + * ### .undefined + * + * Asserts that the target is `undefined`. + * + * expect(undefined).to.be.undefined; + * expect(null).to.not.be.undefined; + * + * @name undefined + * @api public + */ + + Assertion.addProperty('undefined', function () { + this.assert( + undefined === flag(this, 'object') + , 'expected #{this} to be undefined' + , 'expected #{this} not to be undefined' + ); + }); + + /** + * ### .exist + * + * Asserts that the target is neither `null` nor `undefined`. + * + * var foo = 'hi' + * , bar = null + * , baz; + * + * expect(foo).to.exist; + * expect(bar).to.not.exist; + * expect(baz).to.not.exist; + * + * @name exist + * @api public + */ + + Assertion.addProperty('exist', function () { + this.assert( + null != flag(this, 'object') + , 'expected #{this} to exist' + , 'expected #{this} to not exist' + ); + }); + + + /** + * ### .empty + * + * Asserts that the target's length is `0`. For arrays, it checks + * the `length` property. For objects, it gets the count of + * enumerable keys. + * + * expect([]).to.be.empty; + * expect('').to.be.empty; + * expect({}).to.be.empty; + * + * @name empty + * @api public + */ + + Assertion.addProperty('empty', function () { + var obj = flag(this, 'object') + , expected = obj; + + if (Array.isArray(obj) || 'string' === typeof object) { + expected = obj.length; + } else if (typeof obj === 'object') { + expected = Object.keys(obj).length; + } + + this.assert( + !expected + , 'expected #{this} to be empty' + , 'expected #{this} not to be empty' + ); + }); + + /** + * ### .arguments + * + * Asserts that the target is an arguments object. + * + * function test () { + * expect(arguments).to.be.arguments; + * } + * + * @name arguments + * @alias Arguments + * @api public + */ + + function checkArguments () { + var obj = flag(this, 'object') + , type = Object.prototype.toString.call(obj); + this.assert( + '[object Arguments]' === type + , 'expected #{this} to be arguments but got ' + type + , 'expected #{this} to not be arguments' + ); + } + + Assertion.addProperty('arguments', checkArguments); + Assertion.addProperty('Arguments', checkArguments); + + /** + * ### .equal(value) + * + * Asserts that the target is strictly equal (`===`) to `value`. + * Alternately, if the `deep` flag is set, asserts that + * the target is deeply equal to `value`. + * + * expect('hello').to.equal('hello'); + * expect(42).to.equal(42); + * expect(1).to.not.equal(true); + * expect({ foo: 'bar' }).to.not.equal({ foo: 'bar' }); + * expect({ foo: 'bar' }).to.deep.equal({ foo: 'bar' }); + * + * @name equal + * @alias equals + * @alias eq + * @alias deep.equal + * @param {Mixed} value + * @param {String} message _optional_ + * @api public + */ + + function assertEqual (val, msg) { + if (msg) flag(this, 'message', msg); + var obj = flag(this, 'object'); + if (flag(this, 'deep')) { + return this.eql(val); + } else { + this.assert( + val === obj + , 'expected #{this} to equal #{exp}' + , 'expected #{this} to not equal #{exp}' + , val + , this._obj + , true + ); + } + } + + Assertion.addMethod('equal', assertEqual); + Assertion.addMethod('equals', assertEqual); + Assertion.addMethod('eq', assertEqual); + + /** + * ### .eql(value) + * + * Asserts that the target is deeply equal to `value`. + * + * expect({ foo: 'bar' }).to.eql({ foo: 'bar' }); + * expect([ 1, 2, 3 ]).to.eql([ 1, 2, 3 ]); + * + * @name eql + * @alias eqls + * @param {Mixed} value + * @param {String} message _optional_ + * @api public + */ + + function assertEql(obj, msg) { + if (msg) flag(this, 'message', msg); + this.assert( + _.eql(obj, flag(this, 'object')) + , 'expected #{this} to deeply equal #{exp}' + , 'expected #{this} to not deeply equal #{exp}' + , obj + , this._obj + , true + ); + } + + Assertion.addMethod('eql', assertEql); + Assertion.addMethod('eqls', assertEql); + + /** + * ### .above(value) + * + * Asserts that the target is greater than `value`. + * + * expect(10).to.be.above(5); + * + * Can also be used in conjunction with `length` to + * assert a minimum length. The benefit being a + * more informative error message than if the length + * was supplied directly. + * + * expect('foo').to.have.length.above(2); + * expect([ 1, 2, 3 ]).to.have.length.above(2); + * + * @name above + * @alias gt + * @alias greaterThan + * @param {Number} value + * @param {String} message _optional_ + * @api public + */ + + function assertAbove (n, msg) { + if (msg) flag(this, 'message', msg); + var obj = flag(this, 'object'); + if (flag(this, 'doLength')) { + new Assertion(obj, msg).to.have.property('length'); + var len = obj.length; + this.assert( + len > n + , 'expected #{this} to have a length above #{exp} but got #{act}' + , 'expected #{this} to not have a length above #{exp}' + , n + , len + ); + } else { + this.assert( + obj > n + , 'expected #{this} to be above ' + n + , 'expected #{this} to be at most ' + n + ); + } + } + + Assertion.addMethod('above', assertAbove); + Assertion.addMethod('gt', assertAbove); + Assertion.addMethod('greaterThan', assertAbove); + + /** + * ### .least(value) + * + * Asserts that the target is greater than or equal to `value`. + * + * expect(10).to.be.at.least(10); + * + * Can also be used in conjunction with `length` to + * assert a minimum length. The benefit being a + * more informative error message than if the length + * was supplied directly. + * + * expect('foo').to.have.length.of.at.least(2); + * expect([ 1, 2, 3 ]).to.have.length.of.at.least(3); + * + * @name least + * @alias gte + * @param {Number} value + * @param {String} message _optional_ + * @api public + */ + + function assertLeast (n, msg) { + if (msg) flag(this, 'message', msg); + var obj = flag(this, 'object'); + if (flag(this, 'doLength')) { + new Assertion(obj, msg).to.have.property('length'); + var len = obj.length; + this.assert( + len >= n + , 'expected #{this} to have a length at least #{exp} but got #{act}' + , 'expected #{this} to have a length below #{exp}' + , n + , len + ); + } else { + this.assert( + obj >= n + , 'expected #{this} to be at least ' + n + , 'expected #{this} to be below ' + n + ); + } + } + + Assertion.addMethod('least', assertLeast); + Assertion.addMethod('gte', assertLeast); + + /** + * ### .below(value) + * + * Asserts that the target is less than `value`. + * + * expect(5).to.be.below(10); + * + * Can also be used in conjunction with `length` to + * assert a maximum length. The benefit being a + * more informative error message than if the length + * was supplied directly. + * + * expect('foo').to.have.length.below(4); + * expect([ 1, 2, 3 ]).to.have.length.below(4); + * + * @name below + * @alias lt + * @alias lessThan + * @param {Number} value + * @param {String} message _optional_ + * @api public + */ + + function assertBelow (n, msg) { + if (msg) flag(this, 'message', msg); + var obj = flag(this, 'object'); + if (flag(this, 'doLength')) { + new Assertion(obj, msg).to.have.property('length'); + var len = obj.length; + this.assert( + len < n + , 'expected #{this} to have a length below #{exp} but got #{act}' + , 'expected #{this} to not have a length below #{exp}' + , n + , len + ); + } else { + this.assert( + obj < n + , 'expected #{this} to be below ' + n + , 'expected #{this} to be at least ' + n + ); + } + } + + Assertion.addMethod('below', assertBelow); + Assertion.addMethod('lt', assertBelow); + Assertion.addMethod('lessThan', assertBelow); + + /** + * ### .most(value) + * + * Asserts that the target is less than or equal to `value`. + * + * expect(5).to.be.at.most(5); + * + * Can also be used in conjunction with `length` to + * assert a maximum length. The benefit being a + * more informative error message than if the length + * was supplied directly. + * + * expect('foo').to.have.length.of.at.most(4); + * expect([ 1, 2, 3 ]).to.have.length.of.at.most(3); + * + * @name most + * @alias lte + * @param {Number} value + * @param {String} message _optional_ + * @api public + */ + + function assertMost (n, msg) { + if (msg) flag(this, 'message', msg); + var obj = flag(this, 'object'); + if (flag(this, 'doLength')) { + new Assertion(obj, msg).to.have.property('length'); + var len = obj.length; + this.assert( + len <= n + , 'expected #{this} to have a length at most #{exp} but got #{act}' + , 'expected #{this} to have a length above #{exp}' + , n + , len + ); + } else { + this.assert( + obj <= n + , 'expected #{this} to be at most ' + n + , 'expected #{this} to be above ' + n + ); + } + } + + Assertion.addMethod('most', assertMost); + Assertion.addMethod('lte', assertMost); + + /** + * ### .within(start, finish) + * + * Asserts that the target is within a range. + * + * expect(7).to.be.within(5,10); + * + * Can also be used in conjunction with `length` to + * assert a length range. The benefit being a + * more informative error message than if the length + * was supplied directly. + * + * expect('foo').to.have.length.within(2,4); + * expect([ 1, 2, 3 ]).to.have.length.within(2,4); + * + * @name within + * @param {Number} start lowerbound inclusive + * @param {Number} finish upperbound inclusive + * @param {String} message _optional_ + * @api public + */ + + Assertion.addMethod('within', function (start, finish, msg) { + if (msg) flag(this, 'message', msg); + var obj = flag(this, 'object') + , range = start + '..' + finish; + if (flag(this, 'doLength')) { + new Assertion(obj, msg).to.have.property('length'); + var len = obj.length; + this.assert( + len >= start && len <= finish + , 'expected #{this} to have a length within ' + range + , 'expected #{this} to not have a length within ' + range + ); + } else { + this.assert( + obj >= start && obj <= finish + , 'expected #{this} to be within ' + range + , 'expected #{this} to not be within ' + range + ); + } + }); + + /** + * ### .instanceof(constructor) + * + * Asserts that the target is an instance of `constructor`. + * + * var Tea = function (name) { this.name = name; } + * , Chai = new Tea('chai'); + * + * expect(Chai).to.be.an.instanceof(Tea); + * expect([ 1, 2, 3 ]).to.be.instanceof(Array); + * + * @name instanceof + * @param {Constructor} constructor + * @param {String} message _optional_ + * @alias instanceOf + * @api public + */ + + function assertInstanceOf (constructor, msg) { + if (msg) flag(this, 'message', msg); + var name = _.getName(constructor); + this.assert( + flag(this, 'object') instanceof constructor + , 'expected #{this} to be an instance of ' + name + , 'expected #{this} to not be an instance of ' + name + ); + }; + + Assertion.addMethod('instanceof', assertInstanceOf); + Assertion.addMethod('instanceOf', assertInstanceOf); + + /** + * ### .property(name, [value]) + * + * Asserts that the target has a property `name`, optionally asserting that + * the value of that property is strictly equal to `value`. + * If the `deep` flag is set, you can use dot- and bracket-notation for deep + * references into objects and arrays. + * + * // simple referencing + * var obj = { foo: 'bar' }; + * expect(obj).to.have.property('foo'); + * expect(obj).to.have.property('foo', 'bar'); + * + * // deep referencing + * var deepObj = { + * green: { tea: 'matcha' } + * , teas: [ 'chai', 'matcha', { tea: 'konacha' } ] + * }; + + * expect(deepObj).to.have.deep.property('green.tea', 'matcha'); + * expect(deepObj).to.have.deep.property('teas[1]', 'matcha'); + * expect(deepObj).to.have.deep.property('teas[2].tea', 'konacha'); + * + * You can also use an array as the starting point of a `deep.property` + * assertion, or traverse nested arrays. + * + * var arr = [ + * [ 'chai', 'matcha', 'konacha' ] + * , [ { tea: 'chai' } + * , { tea: 'matcha' } + * , { tea: 'konacha' } ] + * ]; + * + * expect(arr).to.have.deep.property('[0][1]', 'matcha'); + * expect(arr).to.have.deep.property('[1][2].tea', 'konacha'); + * + * Furthermore, `property` changes the subject of the assertion + * to be the value of that property from the original object. This + * permits for further chainable assertions on that property. + * + * expect(obj).to.have.property('foo') + * .that.is.a('string'); + * expect(deepObj).to.have.property('green') + * .that.is.an('object') + * .that.deep.equals({ tea: 'matcha' }); + * expect(deepObj).to.have.property('teas') + * .that.is.an('array') + * .with.deep.property('[2]') + * .that.deep.equals({ tea: 'konacha' }); + * + * @name property + * @alias deep.property + * @param {String} name + * @param {Mixed} value (optional) + * @param {String} message _optional_ + * @returns value of property for chaining + * @api public + */ + + Assertion.addMethod('property', function (name, val, msg) { + if (msg) flag(this, 'message', msg); + + var descriptor = flag(this, 'deep') ? 'deep property ' : 'property ' + , negate = flag(this, 'negate') + , obj = flag(this, 'object') + , value = flag(this, 'deep') + ? _.getPathValue(name, obj) + : obj[name]; + + if (negate && undefined !== val) { + if (undefined === value) { + msg = (msg != null) ? msg + ': ' : ''; + throw new Error(msg + _.inspect(obj) + ' has no ' + descriptor + _.inspect(name)); + } + } else { + this.assert( + undefined !== value + , 'expected #{this} to have a ' + descriptor + _.inspect(name) + , 'expected #{this} to not have ' + descriptor + _.inspect(name)); + } + + if (undefined !== val) { + this.assert( + val === value + , 'expected #{this} to have a ' + descriptor + _.inspect(name) + ' of #{exp}, but got #{act}' + , 'expected #{this} to not have a ' + descriptor + _.inspect(name) + ' of #{act}' + , val + , value + ); + } + + flag(this, 'object', value); + }); + + + /** + * ### .ownProperty(name) + * + * Asserts that the target has an own property `name`. + * + * expect('test').to.have.ownProperty('length'); + * + * @name ownProperty + * @alias haveOwnProperty + * @param {String} name + * @param {String} message _optional_ + * @api public + */ + + function assertOwnProperty (name, msg) { + if (msg) flag(this, 'message', msg); + var obj = flag(this, 'object'); + this.assert( + obj.hasOwnProperty(name) + , 'expected #{this} to have own property ' + _.inspect(name) + , 'expected #{this} to not have own property ' + _.inspect(name) + ); + } + + Assertion.addMethod('ownProperty', assertOwnProperty); + Assertion.addMethod('haveOwnProperty', assertOwnProperty); + + /** + * ### .length(value) + * + * Asserts that the target's `length` property has + * the expected value. + * + * expect([ 1, 2, 3]).to.have.length(3); + * expect('foobar').to.have.length(6); + * + * Can also be used as a chain precursor to a value + * comparison for the length property. + * + * expect('foo').to.have.length.above(2); + * expect([ 1, 2, 3 ]).to.have.length.above(2); + * expect('foo').to.have.length.below(4); + * expect([ 1, 2, 3 ]).to.have.length.below(4); + * expect('foo').to.have.length.within(2,4); + * expect([ 1, 2, 3 ]).to.have.length.within(2,4); + * + * @name length + * @alias lengthOf + * @param {Number} length + * @param {String} message _optional_ + * @api public + */ + + function assertLengthChain () { + flag(this, 'doLength', true); + } + + function assertLength (n, msg) { + if (msg) flag(this, 'message', msg); + var obj = flag(this, 'object'); + new Assertion(obj, msg).to.have.property('length'); + var len = obj.length; + + this.assert( + len == n + , 'expected #{this} to have a length of #{exp} but got #{act}' + , 'expected #{this} to not have a length of #{act}' + , n + , len + ); + } + + Assertion.addChainableMethod('length', assertLength, assertLengthChain); + Assertion.addMethod('lengthOf', assertLength, assertLengthChain); + + /** + * ### .match(regexp) + * + * Asserts that the target matches a regular expression. + * + * expect('foobar').to.match(/^foo/); + * + * @name match + * @param {RegExp} RegularExpression + * @param {String} message _optional_ + * @api public + */ + + Assertion.addMethod('match', function (re, msg) { + if (msg) flag(this, 'message', msg); + var obj = flag(this, 'object'); + this.assert( + re.exec(obj) + , 'expected #{this} to match ' + re + , 'expected #{this} not to match ' + re + ); + }); + + /** + * ### .string(string) + * + * Asserts that the string target contains another string. + * + * expect('foobar').to.have.string('bar'); + * + * @name string + * @param {String} string + * @param {String} message _optional_ + * @api public + */ + + Assertion.addMethod('string', function (str, msg) { + if (msg) flag(this, 'message', msg); + var obj = flag(this, 'object'); + new Assertion(obj, msg).is.a('string'); + + this.assert( + ~obj.indexOf(str) + , 'expected #{this} to contain ' + _.inspect(str) + , 'expected #{this} to not contain ' + _.inspect(str) + ); + }); + + + /** + * ### .keys(key1, [key2], [...]) + * + * Asserts that the target has exactly the given keys, or + * asserts the inclusion of some keys when using the + * `include` or `contain` modifiers. + * + * expect({ foo: 1, bar: 2 }).to.have.keys(['foo', 'bar']); + * expect({ foo: 1, bar: 2, baz: 3 }).to.contain.keys('foo', 'bar'); + * + * @name keys + * @alias key + * @param {String...|Array} keys + * @api public + */ + + function assertKeys (keys) { + var obj = flag(this, 'object') + , str + , ok = true; + + keys = keys instanceof Array + ? keys + : Array.prototype.slice.call(arguments); + + if (!keys.length) throw new Error('keys required'); + + var actual = Object.keys(obj) + , len = keys.length; + + // Inclusion + ok = keys.every(function(key){ + return ~actual.indexOf(key); + }); + + // Strict + if (!flag(this, 'negate') && !flag(this, 'contains')) { + ok = ok && keys.length == actual.length; + } + + // Key string + if (len > 1) { + keys = keys.map(function(key){ + return _.inspect(key); + }); + var last = keys.pop(); + str = keys.join(', ') + ', and ' + last; + } else { + str = _.inspect(keys[0]); + } + + // Form + str = (len > 1 ? 'keys ' : 'key ') + str; + + // Have / include + str = (flag(this, 'contains') ? 'contain ' : 'have ') + str; + + // Assertion + this.assert( + ok + , 'expected #{this} to ' + str + , 'expected #{this} to not ' + str + ); + } + + Assertion.addMethod('keys', assertKeys); + Assertion.addMethod('key', assertKeys); + + /** + * ### .throw(constructor) + * + * Asserts that the function target will throw a specific error, or specific type of error + * (as determined using `instanceof`), optionally with a RegExp or string inclusion test + * for the error's message. + * + * var err = new ReferenceError('This is a bad function.'); + * var fn = function () { throw err; } + * expect(fn).to.throw(ReferenceError); + * expect(fn).to.throw(Error); + * expect(fn).to.throw(/bad function/); + * expect(fn).to.not.throw('good function'); + * expect(fn).to.throw(ReferenceError, /bad function/); + * expect(fn).to.throw(err); + * expect(fn).to.not.throw(new RangeError('Out of range.')); + * + * Please note that when a throw expectation is negated, it will check each + * parameter independently, starting with error constructor type. The appropriate way + * to check for the existence of a type of error but for a message that does not match + * is to use `and`. + * + * expect(fn).to.throw(ReferenceError) + * .and.not.throw(/good function/); + * + * @name throw + * @alias throws + * @alias Throw + * @param {ErrorConstructor} constructor + * @param {String|RegExp} expected error message + * @param {String} message _optional_ + * @see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Error#Error_types + * @returns error for chaining (null if no error) + * @api public + */ + + function assertThrows (constructor, errMsg, msg) { + if (msg) flag(this, 'message', msg); + var obj = flag(this, 'object'); + new Assertion(obj, msg).is.a('function'); + + var thrown = false + , desiredError = null + , name = null + , thrownError = null; + + if (arguments.length === 0) { + errMsg = null; + constructor = null; + } else if (constructor && (constructor instanceof RegExp || 'string' === typeof constructor)) { + errMsg = constructor; + constructor = null; + } else if (constructor && constructor instanceof Error) { + desiredError = constructor; + constructor = null; + errMsg = null; + } else if (typeof constructor === 'function') { + name = constructor.prototype.name || constructor.name; + if (name === 'Error' && constructor !== Error) { + name = (new constructor()).name; + } + } else { + constructor = null; + } + + try { + obj(); + } catch (err) { + // first, check desired error + if (desiredError) { + this.assert( + err === desiredError + , 'expected #{this} to throw #{exp} but #{act} was thrown' + , 'expected #{this} to not throw #{exp}' + , (desiredError instanceof Error ? desiredError.toString() : desiredError) + , (err instanceof Error ? err.toString() : err) + ); + + flag(this, 'object', err); + return this; + } + + // next, check constructor + if (constructor) { + this.assert( + err instanceof constructor + , 'expected #{this} to throw #{exp} but #{act} was thrown' + , 'expected #{this} to not throw #{exp} but #{act} was thrown' + , name + , (err instanceof Error ? err.toString() : err) + ); + + if (!errMsg) { + flag(this, 'object', err); + return this; + } + } + + // next, check message + var message = 'object' === _.type(err) && "message" in err + ? err.message + : '' + err; + + if ((message != null) && errMsg && errMsg instanceof RegExp) { + this.assert( + errMsg.exec(message) + , 'expected #{this} to throw error matching #{exp} but got #{act}' + , 'expected #{this} to throw error not matching #{exp}' + , errMsg + , message + ); + + flag(this, 'object', err); + return this; + } else if ((message != null) && errMsg && 'string' === typeof errMsg) { + this.assert( + ~message.indexOf(errMsg) + , 'expected #{this} to throw error including #{exp} but got #{act}' + , 'expected #{this} to throw error not including #{act}' + , errMsg + , message + ); + + flag(this, 'object', err); + return this; + } else { + thrown = true; + thrownError = err; + } + } + + var actuallyGot = '' + , expectedThrown = name !== null + ? name + : desiredError + ? '#{exp}' //_.inspect(desiredError) + : 'an error'; + + if (thrown) { + actuallyGot = ' but #{act} was thrown' + } + + this.assert( + thrown === true + , 'expected #{this} to throw ' + expectedThrown + actuallyGot + , 'expected #{this} to not throw ' + expectedThrown + actuallyGot + , (desiredError instanceof Error ? desiredError.toString() : desiredError) + , (thrownError instanceof Error ? thrownError.toString() : thrownError) + ); + + flag(this, 'object', thrownError); + }; + + Assertion.addMethod('throw', assertThrows); + Assertion.addMethod('throws', assertThrows); + Assertion.addMethod('Throw', assertThrows); + + /** + * ### .respondTo(method) + * + * Asserts that the object or class target will respond to a method. + * + * Klass.prototype.bar = function(){}; + * expect(Klass).to.respondTo('bar'); + * expect(obj).to.respondTo('bar'); + * + * To check if a constructor will respond to a static function, + * set the `itself` flag. + * + * Klass.baz = function(){}; + * expect(Klass).itself.to.respondTo('baz'); + * + * @name respondTo + * @param {String} method + * @param {String} message _optional_ + * @api public + */ + + Assertion.addMethod('respondTo', function (method, msg) { + if (msg) flag(this, 'message', msg); + var obj = flag(this, 'object') + , itself = flag(this, 'itself') + , context = ('function' === _.type(obj) && !itself) + ? obj.prototype[method] + : obj[method]; + + this.assert( + 'function' === typeof context + , 'expected #{this} to respond to ' + _.inspect(method) + , 'expected #{this} to not respond to ' + _.inspect(method) + ); + }); + + /** + * ### .itself + * + * Sets the `itself` flag, later used by the `respondTo` assertion. + * + * function Foo() {} + * Foo.bar = function() {} + * Foo.prototype.baz = function() {} + * + * expect(Foo).itself.to.respondTo('bar'); + * expect(Foo).itself.not.to.respondTo('baz'); + * + * @name itself + * @api public + */ + + Assertion.addProperty('itself', function () { + flag(this, 'itself', true); + }); + + /** + * ### .satisfy(method) + * + * Asserts that the target passes a given truth test. + * + * expect(1).to.satisfy(function(num) { return num > 0; }); + * + * @name satisfy + * @param {Function} matcher + * @param {String} message _optional_ + * @api public + */ + + Assertion.addMethod('satisfy', function (matcher, msg) { + if (msg) flag(this, 'message', msg); + var obj = flag(this, 'object'); + this.assert( + matcher(obj) + , 'expected #{this} to satisfy ' + _.objDisplay(matcher) + , 'expected #{this} to not satisfy' + _.objDisplay(matcher) + , this.negate ? false : true + , matcher(obj) + ); + }); + + /** + * ### .closeTo(expected, delta) + * + * Asserts that the target is equal `expected`, to within a +/- `delta` range. + * + * expect(1.5).to.be.closeTo(1, 0.5); + * + * @name closeTo + * @param {Number} expected + * @param {Number} delta + * @param {String} message _optional_ + * @api public + */ + + Assertion.addMethod('closeTo', function (expected, delta, msg) { + if (msg) flag(this, 'message', msg); + var obj = flag(this, 'object'); + this.assert( + Math.abs(obj - expected) <= delta + , 'expected #{this} to be close to ' + expected + ' +/- ' + delta + , 'expected #{this} not to be close to ' + expected + ' +/- ' + delta + ); + }); + + function isSubsetOf(subset, superset, cmp) { + return subset.every(function(elem) { + if (!cmp) return superset.indexOf(elem) !== -1; + + return superset.some(function(elem2) { + return cmp(elem, elem2); + }); + }) + } + + /** + * ### .members(set) + * + * Asserts that the target is a superset of `set`, + * or that the target and `set` have the same strictly-equal (===) members. + * Alternately, if the `deep` flag is set, set members are compared for deep + * equality. + * + * expect([1, 2, 3]).to.include.members([3, 2]); + * expect([1, 2, 3]).to.not.include.members([3, 2, 8]); + * + * expect([4, 2]).to.have.members([2, 4]); + * expect([5, 2]).to.not.have.members([5, 2, 1]); + * + * expect([{ id: 1 }]).to.deep.include.members([{ id: 1 }]); + * + * @name members + * @param {Array} set + * @param {String} message _optional_ + * @api public + */ + + Assertion.addMethod('members', function (subset, msg) { + if (msg) flag(this, 'message', msg); + var obj = flag(this, 'object'); + + new Assertion(obj).to.be.an('array'); + new Assertion(subset).to.be.an('array'); + + var cmp = flag(this, 'deep') ? _.eql : undefined; + + if (flag(this, 'contains')) { + return this.assert( + isSubsetOf(subset, obj, cmp) + , 'expected #{this} to be a superset of #{act}' + , 'expected #{this} to not be a superset of #{act}' + , obj + , subset + ); + } + + this.assert( + isSubsetOf(obj, subset, cmp) && isSubsetOf(subset, obj, cmp) + , 'expected #{this} to have the same members as #{act}' + , 'expected #{this} to not have the same members as #{act}' + , obj + , subset + ); + }); +}; + +}); +require.register("chai/lib/chai/interface/assert.js", function(exports, require, module){ +/*! + * chai + * Copyright(c) 2011-2014 Jake Luer + * MIT Licensed + */ + + +module.exports = function (chai, util) { + + /*! + * Chai dependencies. + */ + + var Assertion = chai.Assertion + , flag = util.flag; + + /*! + * Module export. + */ + + /** + * ### assert(expression, message) + * + * Write your own test expressions. + * + * assert('foo' !== 'bar', 'foo is not bar'); + * assert(Array.isArray([]), 'empty arrays are arrays'); + * + * @param {Mixed} expression to test for truthiness + * @param {String} message to display on error + * @name assert + * @api public + */ + + var assert = chai.assert = function (express, errmsg) { + var test = new Assertion(null, null, chai.assert); + test.assert( + express + , errmsg + , '[ negation message unavailable ]' + ); + }; + + /** + * ### .fail(actual, expected, [message], [operator]) + * + * Throw a failure. Node.js `assert` module-compatible. + * + * @name fail + * @param {Mixed} actual + * @param {Mixed} expected + * @param {String} message + * @param {String} operator + * @api public + */ + + assert.fail = function (actual, expected, message, operator) { + message = message || 'assert.fail()'; + throw new chai.AssertionError(message, { + actual: actual + , expected: expected + , operator: operator + }, assert.fail); + }; + + /** + * ### .ok(object, [message]) + * + * Asserts that `object` is truthy. + * + * assert.ok('everything', 'everything is ok'); + * assert.ok(false, 'this will fail'); + * + * @name ok + * @param {Mixed} object to test + * @param {String} message + * @api public + */ + + assert.ok = function (val, msg) { + new Assertion(val, msg).is.ok; + }; + + /** + * ### .notOk(object, [message]) + * + * Asserts that `object` is falsy. + * + * assert.notOk('everything', 'this will fail'); + * assert.notOk(false, 'this will pass'); + * + * @name notOk + * @param {Mixed} object to test + * @param {String} message + * @api public + */ + + assert.notOk = function (val, msg) { + new Assertion(val, msg).is.not.ok; + }; + + /** + * ### .equal(actual, expected, [message]) + * + * Asserts non-strict equality (`==`) of `actual` and `expected`. + * + * assert.equal(3, '3', '== coerces values to strings'); + * + * @name equal + * @param {Mixed} actual + * @param {Mixed} expected + * @param {String} message + * @api public + */ + + assert.equal = function (act, exp, msg) { + var test = new Assertion(act, msg, assert.equal); + + test.assert( + exp == flag(test, 'object') + , 'expected #{this} to equal #{exp}' + , 'expected #{this} to not equal #{act}' + , exp + , act + ); + }; + + /** + * ### .notEqual(actual, expected, [message]) + * + * Asserts non-strict inequality (`!=`) of `actual` and `expected`. + * + * assert.notEqual(3, 4, 'these numbers are not equal'); + * + * @name notEqual + * @param {Mixed} actual + * @param {Mixed} expected + * @param {String} message + * @api public + */ + + assert.notEqual = function (act, exp, msg) { + var test = new Assertion(act, msg, assert.notEqual); + + test.assert( + exp != flag(test, 'object') + , 'expected #{this} to not equal #{exp}' + , 'expected #{this} to equal #{act}' + , exp + , act + ); + }; + + /** + * ### .strictEqual(actual, expected, [message]) + * + * Asserts strict equality (`===`) of `actual` and `expected`. + * + * assert.strictEqual(true, true, 'these booleans are strictly equal'); + * + * @name strictEqual + * @param {Mixed} actual + * @param {Mixed} expected + * @param {String} message + * @api public + */ + + assert.strictEqual = function (act, exp, msg) { + new Assertion(act, msg).to.equal(exp); + }; + + /** + * ### .notStrictEqual(actual, expected, [message]) + * + * Asserts strict inequality (`!==`) of `actual` and `expected`. + * + * assert.notStrictEqual(3, '3', 'no coercion for strict equality'); + * + * @name notStrictEqual + * @param {Mixed} actual + * @param {Mixed} expected + * @param {String} message + * @api public + */ + + assert.notStrictEqual = function (act, exp, msg) { + new Assertion(act, msg).to.not.equal(exp); + }; + + /** + * ### .deepEqual(actual, expected, [message]) + * + * Asserts that `actual` is deeply equal to `expected`. + * + * assert.deepEqual({ tea: 'green' }, { tea: 'green' }); + * + * @name deepEqual + * @param {Mixed} actual + * @param {Mixed} expected + * @param {String} message + * @api public + */ + + assert.deepEqual = function (act, exp, msg) { + new Assertion(act, msg).to.eql(exp); + }; + + /** + * ### .notDeepEqual(actual, expected, [message]) + * + * Assert that `actual` is not deeply equal to `expected`. + * + * assert.notDeepEqual({ tea: 'green' }, { tea: 'jasmine' }); + * + * @name notDeepEqual + * @param {Mixed} actual + * @param {Mixed} expected + * @param {String} message + * @api public + */ + + assert.notDeepEqual = function (act, exp, msg) { + new Assertion(act, msg).to.not.eql(exp); + }; + + /** + * ### .isTrue(value, [message]) + * + * Asserts that `value` is true. + * + * var teaServed = true; + * assert.isTrue(teaServed, 'the tea has been served'); + * + * @name isTrue + * @param {Mixed} value + * @param {String} message + * @api public + */ + + assert.isTrue = function (val, msg) { + new Assertion(val, msg).is['true']; + }; + + /** + * ### .isFalse(value, [message]) + * + * Asserts that `value` is false. + * + * var teaServed = false; + * assert.isFalse(teaServed, 'no tea yet? hmm...'); + * + * @name isFalse + * @param {Mixed} value + * @param {String} message + * @api public + */ + + assert.isFalse = function (val, msg) { + new Assertion(val, msg).is['false']; + }; + + /** + * ### .isNull(value, [message]) + * + * Asserts that `value` is null. + * + * assert.isNull(err, 'there was no error'); + * + * @name isNull + * @param {Mixed} value + * @param {String} message + * @api public + */ + + assert.isNull = function (val, msg) { + new Assertion(val, msg).to.equal(null); + }; + + /** + * ### .isNotNull(value, [message]) + * + * Asserts that `value` is not null. + * + * var tea = 'tasty chai'; + * assert.isNotNull(tea, 'great, time for tea!'); + * + * @name isNotNull + * @param {Mixed} value + * @param {String} message + * @api public + */ + + assert.isNotNull = function (val, msg) { + new Assertion(val, msg).to.not.equal(null); + }; + + /** + * ### .isUndefined(value, [message]) + * + * Asserts that `value` is `undefined`. + * + * var tea; + * assert.isUndefined(tea, 'no tea defined'); + * + * @name isUndefined + * @param {Mixed} value + * @param {String} message + * @api public + */ + + assert.isUndefined = function (val, msg) { + new Assertion(val, msg).to.equal(undefined); + }; + + /** + * ### .isDefined(value, [message]) + * + * Asserts that `value` is not `undefined`. + * + * var tea = 'cup of chai'; + * assert.isDefined(tea, 'tea has been defined'); + * + * @name isDefined + * @param {Mixed} value + * @param {String} message + * @api public + */ + + assert.isDefined = function (val, msg) { + new Assertion(val, msg).to.not.equal(undefined); + }; + + /** + * ### .isFunction(value, [message]) + * + * Asserts that `value` is a function. + * + * function serveTea() { return 'cup of tea'; }; + * assert.isFunction(serveTea, 'great, we can have tea now'); + * + * @name isFunction + * @param {Mixed} value + * @param {String} message + * @api public + */ + + assert.isFunction = function (val, msg) { + new Assertion(val, msg).to.be.a('function'); + }; + + /** + * ### .isNotFunction(value, [message]) + * + * Asserts that `value` is _not_ a function. + * + * var serveTea = [ 'heat', 'pour', 'sip' ]; + * assert.isNotFunction(serveTea, 'great, we have listed the steps'); + * + * @name isNotFunction + * @param {Mixed} value + * @param {String} message + * @api public + */ + + assert.isNotFunction = function (val, msg) { + new Assertion(val, msg).to.not.be.a('function'); + }; + + /** + * ### .isObject(value, [message]) + * + * Asserts that `value` is an object (as revealed by + * `Object.prototype.toString`). + * + * var selection = { name: 'Chai', serve: 'with spices' }; + * assert.isObject(selection, 'tea selection is an object'); + * + * @name isObject + * @param {Mixed} value + * @param {String} message + * @api public + */ + + assert.isObject = function (val, msg) { + new Assertion(val, msg).to.be.a('object'); + }; + + /** + * ### .isNotObject(value, [message]) + * + * Asserts that `value` is _not_ an object. + * + * var selection = 'chai' + * assert.isNotObject(selection, 'tea selection is not an object'); + * assert.isNotObject(null, 'null is not an object'); + * + * @name isNotObject + * @param {Mixed} value + * @param {String} message + * @api public + */ + + assert.isNotObject = function (val, msg) { + new Assertion(val, msg).to.not.be.a('object'); + }; + + /** + * ### .isArray(value, [message]) + * + * Asserts that `value` is an array. + * + * var menu = [ 'green', 'chai', 'oolong' ]; + * assert.isArray(menu, 'what kind of tea do we want?'); + * + * @name isArray + * @param {Mixed} value + * @param {String} message + * @api public + */ + + assert.isArray = function (val, msg) { + new Assertion(val, msg).to.be.an('array'); + }; + + /** + * ### .isNotArray(value, [message]) + * + * Asserts that `value` is _not_ an array. + * + * var menu = 'green|chai|oolong'; + * assert.isNotArray(menu, 'what kind of tea do we want?'); + * + * @name isNotArray + * @param {Mixed} value + * @param {String} message + * @api public + */ + + assert.isNotArray = function (val, msg) { + new Assertion(val, msg).to.not.be.an('array'); + }; + + /** + * ### .isString(value, [message]) + * + * Asserts that `value` is a string. + * + * var teaOrder = 'chai'; + * assert.isString(teaOrder, 'order placed'); + * + * @name isString + * @param {Mixed} value + * @param {String} message + * @api public + */ + + assert.isString = function (val, msg) { + new Assertion(val, msg).to.be.a('string'); + }; + + /** + * ### .isNotString(value, [message]) + * + * Asserts that `value` is _not_ a string. + * + * var teaOrder = 4; + * assert.isNotString(teaOrder, 'order placed'); + * + * @name isNotString + * @param {Mixed} value + * @param {String} message + * @api public + */ + + assert.isNotString = function (val, msg) { + new Assertion(val, msg).to.not.be.a('string'); + }; + + /** + * ### .isNumber(value, [message]) + * + * Asserts that `value` is a number. + * + * var cups = 2; + * assert.isNumber(cups, 'how many cups'); + * + * @name isNumber + * @param {Number} value + * @param {String} message + * @api public + */ + + assert.isNumber = function (val, msg) { + new Assertion(val, msg).to.be.a('number'); + }; + + /** + * ### .isNotNumber(value, [message]) + * + * Asserts that `value` is _not_ a number. + * + * var cups = '2 cups please'; + * assert.isNotNumber(cups, 'how many cups'); + * + * @name isNotNumber + * @param {Mixed} value + * @param {String} message + * @api public + */ + + assert.isNotNumber = function (val, msg) { + new Assertion(val, msg).to.not.be.a('number'); + }; + + /** + * ### .isBoolean(value, [message]) + * + * Asserts that `value` is a boolean. + * + * var teaReady = true + * , teaServed = false; + * + * assert.isBoolean(teaReady, 'is the tea ready'); + * assert.isBoolean(teaServed, 'has tea been served'); + * + * @name isBoolean + * @param {Mixed} value + * @param {String} message + * @api public + */ + + assert.isBoolean = function (val, msg) { + new Assertion(val, msg).to.be.a('boolean'); + }; + + /** + * ### .isNotBoolean(value, [message]) + * + * Asserts that `value` is _not_ a boolean. + * + * var teaReady = 'yep' + * , teaServed = 'nope'; + * + * assert.isNotBoolean(teaReady, 'is the tea ready'); + * assert.isNotBoolean(teaServed, 'has tea been served'); + * + * @name isNotBoolean + * @param {Mixed} value + * @param {String} message + * @api public + */ + + assert.isNotBoolean = function (val, msg) { + new Assertion(val, msg).to.not.be.a('boolean'); + }; + + /** + * ### .typeOf(value, name, [message]) + * + * Asserts that `value`'s type is `name`, as determined by + * `Object.prototype.toString`. + * + * assert.typeOf({ tea: 'chai' }, 'object', 'we have an object'); + * assert.typeOf(['chai', 'jasmine'], 'array', 'we have an array'); + * assert.typeOf('tea', 'string', 'we have a string'); + * assert.typeOf(/tea/, 'regexp', 'we have a regular expression'); + * assert.typeOf(null, 'null', 'we have a null'); + * assert.typeOf(undefined, 'undefined', 'we have an undefined'); + * + * @name typeOf + * @param {Mixed} value + * @param {String} name + * @param {String} message + * @api public + */ + + assert.typeOf = function (val, type, msg) { + new Assertion(val, msg).to.be.a(type); + }; + + /** + * ### .notTypeOf(value, name, [message]) + * + * Asserts that `value`'s type is _not_ `name`, as determined by + * `Object.prototype.toString`. + * + * assert.notTypeOf('tea', 'number', 'strings are not numbers'); + * + * @name notTypeOf + * @param {Mixed} value + * @param {String} typeof name + * @param {String} message + * @api public + */ + + assert.notTypeOf = function (val, type, msg) { + new Assertion(val, msg).to.not.be.a(type); + }; + + /** + * ### .instanceOf(object, constructor, [message]) + * + * Asserts that `value` is an instance of `constructor`. + * + * var Tea = function (name) { this.name = name; } + * , chai = new Tea('chai'); + * + * assert.instanceOf(chai, Tea, 'chai is an instance of tea'); + * + * @name instanceOf + * @param {Object} object + * @param {Constructor} constructor + * @param {String} message + * @api public + */ + + assert.instanceOf = function (val, type, msg) { + new Assertion(val, msg).to.be.instanceOf(type); + }; + + /** + * ### .notInstanceOf(object, constructor, [message]) + * + * Asserts `value` is not an instance of `constructor`. + * + * var Tea = function (name) { this.name = name; } + * , chai = new String('chai'); + * + * assert.notInstanceOf(chai, Tea, 'chai is not an instance of tea'); + * + * @name notInstanceOf + * @param {Object} object + * @param {Constructor} constructor + * @param {String} message + * @api public + */ + + assert.notInstanceOf = function (val, type, msg) { + new Assertion(val, msg).to.not.be.instanceOf(type); + }; + + /** + * ### .include(haystack, needle, [message]) + * + * Asserts that `haystack` includes `needle`. Works + * for strings and arrays. + * + * assert.include('foobar', 'bar', 'foobar contains string "bar"'); + * assert.include([ 1, 2, 3 ], 3, 'array contains value'); + * + * @name include + * @param {Array|String} haystack + * @param {Mixed} needle + * @param {String} message + * @api public + */ + + assert.include = function (exp, inc, msg) { + new Assertion(exp, msg, assert.include).include(inc); + }; + + /** + * ### .notInclude(haystack, needle, [message]) + * + * Asserts that `haystack` does not include `needle`. Works + * for strings and arrays. + *i + * assert.notInclude('foobar', 'baz', 'string not include substring'); + * assert.notInclude([ 1, 2, 3 ], 4, 'array not include contain value'); + * + * @name notInclude + * @param {Array|String} haystack + * @param {Mixed} needle + * @param {String} message + * @api public + */ + + assert.notInclude = function (exp, inc, msg) { + new Assertion(exp, msg, assert.notInclude).not.include(inc); + }; + + /** + * ### .match(value, regexp, [message]) + * + * Asserts that `value` matches the regular expression `regexp`. + * + * assert.match('foobar', /^foo/, 'regexp matches'); + * + * @name match + * @param {Mixed} value + * @param {RegExp} regexp + * @param {String} message + * @api public + */ + + assert.match = function (exp, re, msg) { + new Assertion(exp, msg).to.match(re); + }; + + /** + * ### .notMatch(value, regexp, [message]) + * + * Asserts that `value` does not match the regular expression `regexp`. + * + * assert.notMatch('foobar', /^foo/, 'regexp does not match'); + * + * @name notMatch + * @param {Mixed} value + * @param {RegExp} regexp + * @param {String} message + * @api public + */ + + assert.notMatch = function (exp, re, msg) { + new Assertion(exp, msg).to.not.match(re); + }; + + /** + * ### .property(object, property, [message]) + * + * Asserts that `object` has a property named by `property`. + * + * assert.property({ tea: { green: 'matcha' }}, 'tea'); + * + * @name property + * @param {Object} object + * @param {String} property + * @param {String} message + * @api public + */ + + assert.property = function (obj, prop, msg) { + new Assertion(obj, msg).to.have.property(prop); + }; + + /** + * ### .notProperty(object, property, [message]) + * + * Asserts that `object` does _not_ have a property named by `property`. + * + * assert.notProperty({ tea: { green: 'matcha' }}, 'coffee'); + * + * @name notProperty + * @param {Object} object + * @param {String} property + * @param {String} message + * @api public + */ + + assert.notProperty = function (obj, prop, msg) { + new Assertion(obj, msg).to.not.have.property(prop); + }; + + /** + * ### .deepProperty(object, property, [message]) + * + * Asserts that `object` has a property named by `property`, which can be a + * string using dot- and bracket-notation for deep reference. + * + * assert.deepProperty({ tea: { green: 'matcha' }}, 'tea.green'); + * + * @name deepProperty + * @param {Object} object + * @param {String} property + * @param {String} message + * @api public + */ + + assert.deepProperty = function (obj, prop, msg) { + new Assertion(obj, msg).to.have.deep.property(prop); + }; + + /** + * ### .notDeepProperty(object, property, [message]) + * + * Asserts that `object` does _not_ have a property named by `property`, which + * can be a string using dot- and bracket-notation for deep reference. + * + * assert.notDeepProperty({ tea: { green: 'matcha' }}, 'tea.oolong'); + * + * @name notDeepProperty + * @param {Object} object + * @param {String} property + * @param {String} message + * @api public + */ + + assert.notDeepProperty = function (obj, prop, msg) { + new Assertion(obj, msg).to.not.have.deep.property(prop); + }; + + /** + * ### .propertyVal(object, property, value, [message]) + * + * Asserts that `object` has a property named by `property` with value given + * by `value`. + * + * assert.propertyVal({ tea: 'is good' }, 'tea', 'is good'); + * + * @name propertyVal + * @param {Object} object + * @param {String} property + * @param {Mixed} value + * @param {String} message + * @api public + */ + + assert.propertyVal = function (obj, prop, val, msg) { + new Assertion(obj, msg).to.have.property(prop, val); + }; + + /** + * ### .propertyNotVal(object, property, value, [message]) + * + * Asserts that `object` has a property named by `property`, but with a value + * different from that given by `value`. + * + * assert.propertyNotVal({ tea: 'is good' }, 'tea', 'is bad'); + * + * @name propertyNotVal + * @param {Object} object + * @param {String} property + * @param {Mixed} value + * @param {String} message + * @api public + */ + + assert.propertyNotVal = function (obj, prop, val, msg) { + new Assertion(obj, msg).to.not.have.property(prop, val); + }; + + /** + * ### .deepPropertyVal(object, property, value, [message]) + * + * Asserts that `object` has a property named by `property` with value given + * by `value`. `property` can use dot- and bracket-notation for deep + * reference. + * + * assert.deepPropertyVal({ tea: { green: 'matcha' }}, 'tea.green', 'matcha'); + * + * @name deepPropertyVal + * @param {Object} object + * @param {String} property + * @param {Mixed} value + * @param {String} message + * @api public + */ + + assert.deepPropertyVal = function (obj, prop, val, msg) { + new Assertion(obj, msg).to.have.deep.property(prop, val); + }; + + /** + * ### .deepPropertyNotVal(object, property, value, [message]) + * + * Asserts that `object` has a property named by `property`, but with a value + * different from that given by `value`. `property` can use dot- and + * bracket-notation for deep reference. + * + * assert.deepPropertyNotVal({ tea: { green: 'matcha' }}, 'tea.green', 'konacha'); + * + * @name deepPropertyNotVal + * @param {Object} object + * @param {String} property + * @param {Mixed} value + * @param {String} message + * @api public + */ + + assert.deepPropertyNotVal = function (obj, prop, val, msg) { + new Assertion(obj, msg).to.not.have.deep.property(prop, val); + }; + + /** + * ### .lengthOf(object, length, [message]) + * + * Asserts that `object` has a `length` property with the expected value. + * + * assert.lengthOf([1,2,3], 3, 'array has length of 3'); + * assert.lengthOf('foobar', 5, 'string has length of 6'); + * + * @name lengthOf + * @param {Mixed} object + * @param {Number} length + * @param {String} message + * @api public + */ + + assert.lengthOf = function (exp, len, msg) { + new Assertion(exp, msg).to.have.length(len); + }; + + /** + * ### .throws(function, [constructor/string/regexp], [string/regexp], [message]) + * + * Asserts that `function` will throw an error that is an instance of + * `constructor`, or alternately that it will throw an error with message + * matching `regexp`. + * + * assert.throw(fn, 'function throws a reference error'); + * assert.throw(fn, /function throws a reference error/); + * assert.throw(fn, ReferenceError); + * assert.throw(fn, ReferenceError, 'function throws a reference error'); + * assert.throw(fn, ReferenceError, /function throws a reference error/); + * + * @name throws + * @alias throw + * @alias Throw + * @param {Function} function + * @param {ErrorConstructor} constructor + * @param {RegExp} regexp + * @param {String} message + * @see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Error#Error_types + * @api public + */ + + assert.Throw = function (fn, errt, errs, msg) { + if ('string' === typeof errt || errt instanceof RegExp) { + errs = errt; + errt = null; + } + + var assertErr = new Assertion(fn, msg).to.Throw(errt, errs); + return flag(assertErr, 'object'); + }; + + /** + * ### .doesNotThrow(function, [constructor/regexp], [message]) + * + * Asserts that `function` will _not_ throw an error that is an instance of + * `constructor`, or alternately that it will not throw an error with message + * matching `regexp`. + * + * assert.doesNotThrow(fn, Error, 'function does not throw'); + * + * @name doesNotThrow + * @param {Function} function + * @param {ErrorConstructor} constructor + * @param {RegExp} regexp + * @param {String} message + * @see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Error#Error_types + * @api public + */ + + assert.doesNotThrow = function (fn, type, msg) { + if ('string' === typeof type) { + msg = type; + type = null; + } + + new Assertion(fn, msg).to.not.Throw(type); + }; + + /** + * ### .operator(val1, operator, val2, [message]) + * + * Compares two values using `operator`. + * + * assert.operator(1, '<', 2, 'everything is ok'); + * assert.operator(1, '>', 2, 'this will fail'); + * + * @name operator + * @param {Mixed} val1 + * @param {String} operator + * @param {Mixed} val2 + * @param {String} message + * @api public + */ + + assert.operator = function (val, operator, val2, msg) { + if (!~['==', '===', '>', '>=', '<', '<=', '!=', '!=='].indexOf(operator)) { + throw new Error('Invalid operator "' + operator + '"'); + } + var test = new Assertion(eval(val + operator + val2), msg); + test.assert( + true === flag(test, 'object') + , 'expected ' + util.inspect(val) + ' to be ' + operator + ' ' + util.inspect(val2) + , 'expected ' + util.inspect(val) + ' to not be ' + operator + ' ' + util.inspect(val2) ); + }; + + /** + * ### .closeTo(actual, expected, delta, [message]) + * + * Asserts that the target is equal `expected`, to within a +/- `delta` range. + * + * assert.closeTo(1.5, 1, 0.5, 'numbers are close'); + * + * @name closeTo + * @param {Number} actual + * @param {Number} expected + * @param {Number} delta + * @param {String} message + * @api public + */ + + assert.closeTo = function (act, exp, delta, msg) { + new Assertion(act, msg).to.be.closeTo(exp, delta); + }; + + /** + * ### .sameMembers(set1, set2, [message]) + * + * Asserts that `set1` and `set2` have the same members. + * Order is not taken into account. + * + * assert.sameMembers([ 1, 2, 3 ], [ 2, 1, 3 ], 'same members'); + * + * @name sameMembers + * @param {Array} superset + * @param {Array} subset + * @param {String} message + * @api public + */ + + assert.sameMembers = function (set1, set2, msg) { + new Assertion(set1, msg).to.have.same.members(set2); + } + + /** + * ### .includeMembers(superset, subset, [message]) + * + * Asserts that `subset` is included in `superset`. + * Order is not taken into account. + * + * assert.includeMembers([ 1, 2, 3 ], [ 2, 1 ], 'include members'); + * + * @name includeMembers + * @param {Array} superset + * @param {Array} subset + * @param {String} message + * @api public + */ + + assert.includeMembers = function (superset, subset, msg) { + new Assertion(superset, msg).to.include.members(subset); + } + + /*! + * Undocumented / untested + */ + + assert.ifError = function (val, msg) { + new Assertion(val, msg).to.not.be.ok; + }; + + /*! + * Aliases. + */ + + (function alias(name, as){ + assert[as] = assert[name]; + return alias; + }) + ('Throw', 'throw') + ('Throw', 'throws'); +}; + +}); +require.register("chai/lib/chai/interface/expect.js", function(exports, require, module){ +/*! + * chai + * Copyright(c) 2011-2014 Jake Luer + * MIT Licensed + */ + +module.exports = function (chai, util) { + chai.expect = function (val, message) { + return new chai.Assertion(val, message); + }; +}; + + +}); +require.register("chai/lib/chai/interface/should.js", function(exports, require, module){ +/*! + * chai + * Copyright(c) 2011-2014 Jake Luer + * MIT Licensed + */ + +module.exports = function (chai, util) { + var Assertion = chai.Assertion; + + function loadShould () { + // explicitly define this method as function as to have it's name to include as `ssfi` + function shouldGetter() { + if (this instanceof String || this instanceof Number) { + return new Assertion(this.constructor(this), null, shouldGetter); + } else if (this instanceof Boolean) { + return new Assertion(this == true, null, shouldGetter); + } + return new Assertion(this, null, shouldGetter); + } + function shouldSetter(value) { + // See https://github.com/chaijs/chai/issues/86: this makes + // `whatever.should = someValue` actually set `someValue`, which is + // especially useful for `global.should = require('chai').should()`. + // + // Note that we have to use [[DefineProperty]] instead of [[Put]] + // since otherwise we would trigger this very setter! + Object.defineProperty(this, 'should', { + value: value, + enumerable: true, + configurable: true, + writable: true + }); + } + // modify Object.prototype to have `should` + Object.defineProperty(Object.prototype, 'should', { + set: shouldSetter + , get: shouldGetter + , configurable: true + }); + + var should = {}; + + should.equal = function (val1, val2, msg) { + new Assertion(val1, msg).to.equal(val2); + }; + + should.Throw = function (fn, errt, errs, msg) { + new Assertion(fn, msg).to.Throw(errt, errs); + }; + + should.exist = function (val, msg) { + new Assertion(val, msg).to.exist; + } + + // negation + should.not = {} + + should.not.equal = function (val1, val2, msg) { + new Assertion(val1, msg).to.not.equal(val2); + }; + + should.not.Throw = function (fn, errt, errs, msg) { + new Assertion(fn, msg).to.not.Throw(errt, errs); + }; + + should.not.exist = function (val, msg) { + new Assertion(val, msg).to.not.exist; + } + + should['throw'] = should['Throw']; + should.not['throw'] = should.not['Throw']; + + return should; + }; + + chai.should = loadShould; + chai.Should = loadShould; +}; + +}); +require.register("chai/lib/chai/utils/addChainableMethod.js", function(exports, require, module){ +/*! + * Chai - addChainingMethod utility + * Copyright(c) 2012-2014 Jake Luer + * MIT Licensed + */ + +/*! + * Module dependencies + */ + +var transferFlags = require('./transferFlags'); +var flag = require('./flag'); +var config = require('../config'); + +/*! + * Module variables + */ + +// Check whether `__proto__` is supported +var hasProtoSupport = '__proto__' in Object; + +// Without `__proto__` support, this module will need to add properties to a function. +// However, some Function.prototype methods cannot be overwritten, +// and there seems no easy cross-platform way to detect them (@see chaijs/chai/issues/69). +var excludeNames = /^(?:length|name|arguments|caller)$/; + +// Cache `Function` properties +var call = Function.prototype.call, + apply = Function.prototype.apply; + +/** + * ### addChainableMethod (ctx, name, method, chainingBehavior) + * + * Adds a method to an object, such that the method can also be chained. + * + * utils.addChainableMethod(chai.Assertion.prototype, 'foo', function (str) { + * var obj = utils.flag(this, 'object'); + * new chai.Assertion(obj).to.be.equal(str); + * }); + * + * Can also be accessed directly from `chai.Assertion`. + * + * chai.Assertion.addChainableMethod('foo', fn, chainingBehavior); + * + * The result can then be used as both a method assertion, executing both `method` and + * `chainingBehavior`, or as a language chain, which only executes `chainingBehavior`. + * + * expect(fooStr).to.be.foo('bar'); + * expect(fooStr).to.be.foo.equal('foo'); + * + * @param {Object} ctx object to which the method is added + * @param {String} name of method to add + * @param {Function} method function to be used for `name`, when called + * @param {Function} chainingBehavior function to be called every time the property is accessed + * @name addChainableMethod + * @api public + */ + +module.exports = function (ctx, name, method, chainingBehavior) { + if (typeof chainingBehavior !== 'function') { + chainingBehavior = function () { }; + } + + var chainableBehavior = { + method: method + , chainingBehavior: chainingBehavior + }; + + // save the methods so we can overwrite them later, if we need to. + if (!ctx.__methods) { + ctx.__methods = {}; + } + ctx.__methods[name] = chainableBehavior; + + Object.defineProperty(ctx, name, + { get: function () { + chainableBehavior.chainingBehavior.call(this); + + var assert = function assert() { + var old_ssfi = flag(this, 'ssfi'); + if (old_ssfi && config.includeStack === false) + flag(this, 'ssfi', assert); + var result = chainableBehavior.method.apply(this, arguments); + return result === undefined ? this : result; + }; + + // Use `__proto__` if available + if (hasProtoSupport) { + // Inherit all properties from the object by replacing the `Function` prototype + var prototype = assert.__proto__ = Object.create(this); + // Restore the `call` and `apply` methods from `Function` + prototype.call = call; + prototype.apply = apply; + } + // Otherwise, redefine all properties (slow!) + else { + var asserterNames = Object.getOwnPropertyNames(ctx); + asserterNames.forEach(function (asserterName) { + if (!excludeNames.test(asserterName)) { + var pd = Object.getOwnPropertyDescriptor(ctx, asserterName); + Object.defineProperty(assert, asserterName, pd); + } + }); + } + + transferFlags(this, assert); + return assert; + } + , configurable: true + }); +}; + +}); +require.register("chai/lib/chai/utils/addMethod.js", function(exports, require, module){ +/*! + * Chai - addMethod utility + * Copyright(c) 2012-2014 Jake Luer + * MIT Licensed + */ + +var config = require('../config'); + +/** + * ### .addMethod (ctx, name, method) + * + * Adds a method to the prototype of an object. + * + * utils.addMethod(chai.Assertion.prototype, 'foo', function (str) { + * var obj = utils.flag(this, 'object'); + * new chai.Assertion(obj).to.be.equal(str); + * }); + * + * Can also be accessed directly from `chai.Assertion`. + * + * chai.Assertion.addMethod('foo', fn); + * + * Then can be used as any other assertion. + * + * expect(fooStr).to.be.foo('bar'); + * + * @param {Object} ctx object to which the method is added + * @param {String} name of method to add + * @param {Function} method function to be used for name + * @name addMethod + * @api public + */ +var flag = require('./flag'); + +module.exports = function (ctx, name, method) { + ctx[name] = function () { + var old_ssfi = flag(this, 'ssfi'); + if (old_ssfi && config.includeStack === false) + flag(this, 'ssfi', ctx[name]); + var result = method.apply(this, arguments); + return result === undefined ? this : result; + }; +}; + +}); +require.register("chai/lib/chai/utils/addProperty.js", function(exports, require, module){ +/*! + * Chai - addProperty utility + * Copyright(c) 2012-2014 Jake Luer + * MIT Licensed + */ + +/** + * ### addProperty (ctx, name, getter) + * + * Adds a property to the prototype of an object. + * + * utils.addProperty(chai.Assertion.prototype, 'foo', function () { + * var obj = utils.flag(this, 'object'); + * new chai.Assertion(obj).to.be.instanceof(Foo); + * }); + * + * Can also be accessed directly from `chai.Assertion`. + * + * chai.Assertion.addProperty('foo', fn); + * + * Then can be used as any other assertion. + * + * expect(myFoo).to.be.foo; + * + * @param {Object} ctx object to which the property is added + * @param {String} name of property to add + * @param {Function} getter function to be used for name + * @name addProperty + * @api public + */ + +module.exports = function (ctx, name, getter) { + Object.defineProperty(ctx, name, + { get: function () { + var result = getter.call(this); + return result === undefined ? this : result; + } + , configurable: true + }); +}; + +}); +require.register("chai/lib/chai/utils/flag.js", function(exports, require, module){ +/*! + * Chai - flag utility + * Copyright(c) 2012-2014 Jake Luer + * MIT Licensed + */ + +/** + * ### flag(object ,key, [value]) + * + * Get or set a flag value on an object. If a + * value is provided it will be set, else it will + * return the currently set value or `undefined` if + * the value is not set. + * + * utils.flag(this, 'foo', 'bar'); // setter + * utils.flag(this, 'foo'); // getter, returns `bar` + * + * @param {Object} object (constructed Assertion + * @param {String} key + * @param {Mixed} value (optional) + * @name flag + * @api private + */ + +module.exports = function (obj, key, value) { + var flags = obj.__flags || (obj.__flags = Object.create(null)); + if (arguments.length === 3) { + flags[key] = value; + } else { + return flags[key]; + } +}; + +}); +require.register("chai/lib/chai/utils/getActual.js", function(exports, require, module){ +/*! + * Chai - getActual utility + * Copyright(c) 2012-2014 Jake Luer + * MIT Licensed + */ + +/** + * # getActual(object, [actual]) + * + * Returns the `actual` value for an Assertion + * + * @param {Object} object (constructed Assertion) + * @param {Arguments} chai.Assertion.prototype.assert arguments + */ + +module.exports = function (obj, args) { + return args.length > 4 ? args[4] : obj._obj; +}; + +}); +require.register("chai/lib/chai/utils/getEnumerableProperties.js", function(exports, require, module){ +/*! + * Chai - getEnumerableProperties utility + * Copyright(c) 2012-2014 Jake Luer + * MIT Licensed + */ + +/** + * ### .getEnumerableProperties(object) + * + * This allows the retrieval of enumerable property names of an object, + * inherited or not. + * + * @param {Object} object + * @returns {Array} + * @name getEnumerableProperties + * @api public + */ + +module.exports = function getEnumerableProperties(object) { + var result = []; + for (var name in object) { + result.push(name); + } + return result; +}; + +}); +require.register("chai/lib/chai/utils/getMessage.js", function(exports, require, module){ +/*! + * Chai - message composition utility + * Copyright(c) 2012-2014 Jake Luer + * MIT Licensed + */ + +/*! + * Module dependancies + */ + +var flag = require('./flag') + , getActual = require('./getActual') + , inspect = require('./inspect') + , objDisplay = require('./objDisplay'); + +/** + * ### .getMessage(object, message, negateMessage) + * + * Construct the error message based on flags + * and template tags. Template tags will return + * a stringified inspection of the object referenced. + * + * Message template tags: + * - `#{this}` current asserted object + * - `#{act}` actual value + * - `#{exp}` expected value + * + * @param {Object} object (constructed Assertion) + * @param {Arguments} chai.Assertion.prototype.assert arguments + * @name getMessage + * @api public + */ + +module.exports = function (obj, args) { + var negate = flag(obj, 'negate') + , val = flag(obj, 'object') + , expected = args[3] + , actual = getActual(obj, args) + , msg = negate ? args[2] : args[1] + , flagMsg = flag(obj, 'message'); + + msg = msg || ''; + msg = msg + .replace(/#{this}/g, objDisplay(val)) + .replace(/#{act}/g, objDisplay(actual)) + .replace(/#{exp}/g, objDisplay(expected)); + + return flagMsg ? flagMsg + ': ' + msg : msg; +}; + +}); +require.register("chai/lib/chai/utils/getName.js", function(exports, require, module){ +/*! + * Chai - getName utility + * Copyright(c) 2012-2014 Jake Luer + * MIT Licensed + */ + +/** + * # getName(func) + * + * Gets the name of a function, in a cross-browser way. + * + * @param {Function} a function (usually a constructor) + */ + +module.exports = function (func) { + if (func.name) return func.name; + + var match = /^\s?function ([^(]*)\(/.exec(func); + return match && match[1] ? match[1] : ""; +}; + +}); +require.register("chai/lib/chai/utils/getPathValue.js", function(exports, require, module){ +/*! + * Chai - getPathValue utility + * Copyright(c) 2012-2014 Jake Luer + * @see https://github.com/logicalparadox/filtr + * MIT Licensed + */ + +/** + * ### .getPathValue(path, object) + * + * This allows the retrieval of values in an + * object given a string path. + * + * var obj = { + * prop1: { + * arr: ['a', 'b', 'c'] + * , str: 'Hello' + * } + * , prop2: { + * arr: [ { nested: 'Universe' } ] + * , str: 'Hello again!' + * } + * } + * + * The following would be the results. + * + * getPathValue('prop1.str', obj); // Hello + * getPathValue('prop1.att[2]', obj); // b + * getPathValue('prop2.arr[0].nested', obj); // Universe + * + * @param {String} path + * @param {Object} object + * @returns {Object} value or `undefined` + * @name getPathValue + * @api public + */ + +var getPathValue = module.exports = function (path, obj) { + var parsed = parsePath(path); + return _getPathValue(parsed, obj); +}; + +/*! + * ## parsePath(path) + * + * Helper function used to parse string object + * paths. Use in conjunction with `_getPathValue`. + * + * var parsed = parsePath('myobject.property.subprop'); + * + * ### Paths: + * + * * Can be as near infinitely deep and nested + * * Arrays are also valid using the formal `myobject.document[3].property`. + * + * @param {String} path + * @returns {Object} parsed + * @api private + */ + +function parsePath (path) { + var str = path.replace(/\[/g, '.[') + , parts = str.match(/(\\\.|[^.]+?)+/g); + return parts.map(function (value) { + var re = /\[(\d+)\]$/ + , mArr = re.exec(value) + if (mArr) return { i: parseFloat(mArr[1]) }; + else return { p: value }; + }); +}; + +/*! + * ## _getPathValue(parsed, obj) + * + * Helper companion function for `.parsePath` that returns + * the value located at the parsed address. + * + * var value = getPathValue(parsed, obj); + * + * @param {Object} parsed definition from `parsePath`. + * @param {Object} object to search against + * @returns {Object|Undefined} value + * @api private + */ + +function _getPathValue (parsed, obj) { + var tmp = obj + , res; + for (var i = 0, l = parsed.length; i < l; i++) { + var part = parsed[i]; + if (tmp) { + if ('undefined' !== typeof part.p) + tmp = tmp[part.p]; + else if ('undefined' !== typeof part.i) + tmp = tmp[part.i]; + if (i == (l - 1)) res = tmp; + } else { + res = undefined; + } + } + return res; +}; + +}); +require.register("chai/lib/chai/utils/getProperties.js", function(exports, require, module){ +/*! + * Chai - getProperties utility + * Copyright(c) 2012-2014 Jake Luer + * MIT Licensed + */ + +/** + * ### .getProperties(object) + * + * This allows the retrieval of property names of an object, enumerable or not, + * inherited or not. + * + * @param {Object} object + * @returns {Array} + * @name getProperties + * @api public + */ + +module.exports = function getProperties(object) { + var result = Object.getOwnPropertyNames(subject); + + function addProperty(property) { + if (result.indexOf(property) === -1) { + result.push(property); + } + } + + var proto = Object.getPrototypeOf(subject); + while (proto !== null) { + Object.getOwnPropertyNames(proto).forEach(addProperty); + proto = Object.getPrototypeOf(proto); + } + + return result; +}; + +}); +require.register("chai/lib/chai/utils/index.js", function(exports, require, module){ +/*! + * chai + * Copyright(c) 2011 Jake Luer + * MIT Licensed + */ + +/*! + * Main exports + */ + +var exports = module.exports = {}; + +/*! + * test utility + */ + +exports.test = require('./test'); + +/*! + * type utility + */ + +exports.type = require('./type'); + +/*! + * message utility + */ + +exports.getMessage = require('./getMessage'); + +/*! + * actual utility + */ + +exports.getActual = require('./getActual'); + +/*! + * Inspect util + */ + +exports.inspect = require('./inspect'); + +/*! + * Object Display util + */ + +exports.objDisplay = require('./objDisplay'); + +/*! + * Flag utility + */ + +exports.flag = require('./flag'); + +/*! + * Flag transferring utility + */ + +exports.transferFlags = require('./transferFlags'); + +/*! + * Deep equal utility + */ + +exports.eql = require('deep-eql'); + +/*! + * Deep path value + */ + +exports.getPathValue = require('./getPathValue'); + +/*! + * Function name + */ + +exports.getName = require('./getName'); + +/*! + * add Property + */ + +exports.addProperty = require('./addProperty'); + +/*! + * add Method + */ + +exports.addMethod = require('./addMethod'); + +/*! + * overwrite Property + */ + +exports.overwriteProperty = require('./overwriteProperty'); + +/*! + * overwrite Method + */ + +exports.overwriteMethod = require('./overwriteMethod'); + +/*! + * Add a chainable method + */ + +exports.addChainableMethod = require('./addChainableMethod'); + +/*! + * Overwrite chainable method + */ + +exports.overwriteChainableMethod = require('./overwriteChainableMethod'); + + +}); +require.register("chai/lib/chai/utils/inspect.js", function(exports, require, module){ +// This is (almost) directly from Node.js utils +// https://github.com/joyent/node/blob/f8c335d0caf47f16d31413f89aa28eda3878e3aa/lib/util.js + +var getName = require('./getName'); +var getProperties = require('./getProperties'); +var getEnumerableProperties = require('./getEnumerableProperties'); + +module.exports = inspect; + +/** + * Echos the value of a value. Trys to print the value out + * in the best way possible given the different types. + * + * @param {Object} obj The object to print out. + * @param {Boolean} showHidden Flag that shows hidden (not enumerable) + * properties of objects. + * @param {Number} depth Depth in which to descend in object. Default is 2. + * @param {Boolean} colors Flag to turn on ANSI escape codes to color the + * output. Default is false (no coloring). + */ +function inspect(obj, showHidden, depth, colors) { + var ctx = { + showHidden: showHidden, + seen: [], + stylize: function (str) { return str; } + }; + return formatValue(ctx, obj, (typeof depth === 'undefined' ? 2 : depth)); +} + +// https://gist.github.com/1044128/ +var getOuterHTML = function(element) { + if ('outerHTML' in element) return element.outerHTML; + var ns = "http://www.w3.org/1999/xhtml"; + var container = document.createElementNS(ns, '_'); + var elemProto = (window.HTMLElement || window.Element).prototype; + var xmlSerializer = new XMLSerializer(); + var html; + if (document.xmlVersion) { + return xmlSerializer.serializeToString(element); + } else { + container.appendChild(element.cloneNode(false)); + html = container.innerHTML.replace('><', '>' + element.innerHTML + '<'); + container.innerHTML = ''; + return html; + } +}; + +// Returns true if object is a DOM element. +var isDOMElement = function (object) { + if (typeof HTMLElement === 'object') { + return object instanceof HTMLElement; + } else { + return object && + typeof object === 'object' && + object.nodeType === 1 && + typeof object.nodeName === 'string'; + } +}; + +function formatValue(ctx, value, recurseTimes) { + // Provide a hook for user-specified inspect functions. + // Check that value is an object with an inspect function on it + if (value && typeof value.inspect === 'function' && + // Filter out the util module, it's inspect function is special + value.inspect !== exports.inspect && + // Also filter out any prototype objects using the circular check. + !(value.constructor && value.constructor.prototype === value)) { + var ret = value.inspect(recurseTimes); + if (typeof ret !== 'string') { + ret = formatValue(ctx, ret, recurseTimes); + } + return ret; + } + + // Primitive types cannot have properties + var primitive = formatPrimitive(ctx, value); + if (primitive) { + return primitive; + } + + // If it's DOM elem, get outer HTML. + if (isDOMElement(value)) { + return getOuterHTML(value); + } + + // Look up the keys of the object. + var visibleKeys = getEnumerableProperties(value); + var keys = ctx.showHidden ? getProperties(value) : visibleKeys; + + // Some type of object without properties can be shortcutted. + // In IE, errors have a single `stack` property, or if they are vanilla `Error`, + // a `stack` plus `description` property; ignore those for consistency. + if (keys.length === 0 || (isError(value) && ( + (keys.length === 1 && keys[0] === 'stack') || + (keys.length === 2 && keys[0] === 'description' && keys[1] === 'stack') + ))) { + if (typeof value === 'function') { + var name = getName(value); + var nameSuffix = name ? ': ' + name : ''; + return ctx.stylize('[Function' + nameSuffix + ']', 'special'); + } + if (isRegExp(value)) { + return ctx.stylize(RegExp.prototype.toString.call(value), 'regexp'); + } + if (isDate(value)) { + return ctx.stylize(Date.prototype.toUTCString.call(value), 'date'); + } + if (isError(value)) { + return formatError(value); + } + } + + var base = '', array = false, braces = ['{', '}']; + + // Make Array say that they are Array + if (isArray(value)) { + array = true; + braces = ['[', ']']; + } + + // Make functions say that they are functions + if (typeof value === 'function') { + var name = getName(value); + var nameSuffix = name ? ': ' + name : ''; + base = ' [Function' + nameSuffix + ']'; + } + + // Make RegExps say that they are RegExps + if (isRegExp(value)) { + base = ' ' + RegExp.prototype.toString.call(value); + } + + // Make dates with properties first say the date + if (isDate(value)) { + base = ' ' + Date.prototype.toUTCString.call(value); + } + + // Make error with message first say the error + if (isError(value)) { + return formatError(value); + } + + if (keys.length === 0 && (!array || value.length == 0)) { + return braces[0] + base + braces[1]; + } + + if (recurseTimes < 0) { + if (isRegExp(value)) { + return ctx.stylize(RegExp.prototype.toString.call(value), 'regexp'); + } else { + return ctx.stylize('[Object]', 'special'); + } + } + + ctx.seen.push(value); + + var output; + if (array) { + output = formatArray(ctx, value, recurseTimes, visibleKeys, keys); + } else { + output = keys.map(function(key) { + return formatProperty(ctx, value, recurseTimes, visibleKeys, key, array); + }); + } + + ctx.seen.pop(); + + return reduceToSingleString(output, base, braces); +} + + +function formatPrimitive(ctx, value) { + switch (typeof value) { + case 'undefined': + return ctx.stylize('undefined', 'undefined'); + + case 'string': + var simple = '\'' + JSON.stringify(value).replace(/^"|"$/g, '') + .replace(/'/g, "\\'") + .replace(/\\"/g, '"') + '\''; + return ctx.stylize(simple, 'string'); + + case 'number': + return ctx.stylize('' + value, 'number'); + + case 'boolean': + return ctx.stylize('' + value, 'boolean'); + } + // For some reason typeof null is "object", so special case here. + if (value === null) { + return ctx.stylize('null', 'null'); + } +} + + +function formatError(value) { + return '[' + Error.prototype.toString.call(value) + ']'; +} + + +function formatArray(ctx, value, recurseTimes, visibleKeys, keys) { + var output = []; + for (var i = 0, l = value.length; i < l; ++i) { + if (Object.prototype.hasOwnProperty.call(value, String(i))) { + output.push(formatProperty(ctx, value, recurseTimes, visibleKeys, + String(i), true)); + } else { + output.push(''); + } + } + keys.forEach(function(key) { + if (!key.match(/^\d+$/)) { + output.push(formatProperty(ctx, value, recurseTimes, visibleKeys, + key, true)); + } + }); + return output; +} + + +function formatProperty(ctx, value, recurseTimes, visibleKeys, key, array) { + var name, str; + if (value.__lookupGetter__) { + if (value.__lookupGetter__(key)) { + if (value.__lookupSetter__(key)) { + str = ctx.stylize('[Getter/Setter]', 'special'); + } else { + str = ctx.stylize('[Getter]', 'special'); + } + } else { + if (value.__lookupSetter__(key)) { + str = ctx.stylize('[Setter]', 'special'); + } + } + } + if (visibleKeys.indexOf(key) < 0) { + name = '[' + key + ']'; + } + if (!str) { + if (ctx.seen.indexOf(value[key]) < 0) { + if (recurseTimes === null) { + str = formatValue(ctx, value[key], null); + } else { + str = formatValue(ctx, value[key], recurseTimes - 1); + } + if (str.indexOf('\n') > -1) { + if (array) { + str = str.split('\n').map(function(line) { + return ' ' + line; + }).join('\n').substr(2); + } else { + str = '\n' + str.split('\n').map(function(line) { + return ' ' + line; + }).join('\n'); + } + } + } else { + str = ctx.stylize('[Circular]', 'special'); + } + } + if (typeof name === 'undefined') { + if (array && key.match(/^\d+$/)) { + return str; + } + name = JSON.stringify('' + key); + if (name.match(/^"([a-zA-Z_][a-zA-Z_0-9]*)"$/)) { + name = name.substr(1, name.length - 2); + name = ctx.stylize(name, 'name'); + } else { + name = name.replace(/'/g, "\\'") + .replace(/\\"/g, '"') + .replace(/(^"|"$)/g, "'"); + name = ctx.stylize(name, 'string'); + } + } + + return name + ': ' + str; +} + + +function reduceToSingleString(output, base, braces) { + var numLinesEst = 0; + var length = output.reduce(function(prev, cur) { + numLinesEst++; + if (cur.indexOf('\n') >= 0) numLinesEst++; + return prev + cur.length + 1; + }, 0); + + if (length > 60) { + return braces[0] + + (base === '' ? '' : base + '\n ') + + ' ' + + output.join(',\n ') + + ' ' + + braces[1]; + } + + return braces[0] + base + ' ' + output.join(', ') + ' ' + braces[1]; +} + +function isArray(ar) { + return Array.isArray(ar) || + (typeof ar === 'object' && objectToString(ar) === '[object Array]'); +} + +function isRegExp(re) { + return typeof re === 'object' && objectToString(re) === '[object RegExp]'; +} + +function isDate(d) { + return typeof d === 'object' && objectToString(d) === '[object Date]'; +} + +function isError(e) { + return typeof e === 'object' && objectToString(e) === '[object Error]'; +} + +function objectToString(o) { + return Object.prototype.toString.call(o); +} + +}); +require.register("chai/lib/chai/utils/objDisplay.js", function(exports, require, module){ +/*! + * Chai - flag utility + * Copyright(c) 2012-2014 Jake Luer + * MIT Licensed + */ + +/*! + * Module dependancies + */ + +var inspect = require('./inspect'); +var config = require('../config'); + +/** + * ### .objDisplay (object) + * + * Determines if an object or an array matches + * criteria to be inspected in-line for error + * messages or should be truncated. + * + * @param {Mixed} javascript object to inspect + * @name objDisplay + * @api public + */ + +module.exports = function (obj) { + var str = inspect(obj) + , type = Object.prototype.toString.call(obj); + + if (config.truncateThreshold && str.length >= config.truncateThreshold) { + if (type === '[object Function]') { + return !obj.name || obj.name === '' + ? '[Function]' + : '[Function: ' + obj.name + ']'; + } else if (type === '[object Array]') { + return '[ Array(' + obj.length + ') ]'; + } else if (type === '[object Object]') { + var keys = Object.keys(obj) + , kstr = keys.length > 2 + ? keys.splice(0, 2).join(', ') + ', ...' + : keys.join(', '); + return '{ Object (' + kstr + ') }'; + } else { + return str; + } + } else { + return str; + } +}; + +}); +require.register("chai/lib/chai/utils/overwriteMethod.js", function(exports, require, module){ +/*! + * Chai - overwriteMethod utility + * Copyright(c) 2012-2014 Jake Luer + * MIT Licensed + */ + +/** + * ### overwriteMethod (ctx, name, fn) + * + * Overwites an already existing method and provides + * access to previous function. Must return function + * to be used for name. + * + * utils.overwriteMethod(chai.Assertion.prototype, 'equal', function (_super) { + * return function (str) { + * var obj = utils.flag(this, 'object'); + * if (obj instanceof Foo) { + * new chai.Assertion(obj.value).to.equal(str); + * } else { + * _super.apply(this, arguments); + * } + * } + * }); + * + * Can also be accessed directly from `chai.Assertion`. + * + * chai.Assertion.overwriteMethod('foo', fn); + * + * Then can be used as any other assertion. + * + * expect(myFoo).to.equal('bar'); + * + * @param {Object} ctx object whose method is to be overwritten + * @param {String} name of method to overwrite + * @param {Function} method function that returns a function to be used for name + * @name overwriteMethod + * @api public + */ + +module.exports = function (ctx, name, method) { + var _method = ctx[name] + , _super = function () { return this; }; + + if (_method && 'function' === typeof _method) + _super = _method; + + ctx[name] = function () { + var result = method(_super).apply(this, arguments); + return result === undefined ? this : result; + } +}; + +}); +require.register("chai/lib/chai/utils/overwriteProperty.js", function(exports, require, module){ +/*! + * Chai - overwriteProperty utility + * Copyright(c) 2012-2014 Jake Luer + * MIT Licensed + */ + +/** + * ### overwriteProperty (ctx, name, fn) + * + * Overwites an already existing property getter and provides + * access to previous value. Must return function to use as getter. + * + * utils.overwriteProperty(chai.Assertion.prototype, 'ok', function (_super) { + * return function () { + * var obj = utils.flag(this, 'object'); + * if (obj instanceof Foo) { + * new chai.Assertion(obj.name).to.equal('bar'); + * } else { + * _super.call(this); + * } + * } + * }); + * + * + * Can also be accessed directly from `chai.Assertion`. + * + * chai.Assertion.overwriteProperty('foo', fn); + * + * Then can be used as any other assertion. + * + * expect(myFoo).to.be.ok; + * + * @param {Object} ctx object whose property is to be overwritten + * @param {String} name of property to overwrite + * @param {Function} getter function that returns a getter function to be used for name + * @name overwriteProperty + * @api public + */ + +module.exports = function (ctx, name, getter) { + var _get = Object.getOwnPropertyDescriptor(ctx, name) + , _super = function () {}; + + if (_get && 'function' === typeof _get.get) + _super = _get.get + + Object.defineProperty(ctx, name, + { get: function () { + var result = getter(_super).call(this); + return result === undefined ? this : result; + } + , configurable: true + }); +}; + +}); +require.register("chai/lib/chai/utils/overwriteChainableMethod.js", function(exports, require, module){ +/*! + * Chai - overwriteChainableMethod utility + * Copyright(c) 2012-2014 Jake Luer + * MIT Licensed + */ + +/** + * ### overwriteChainableMethod (ctx, name, fn) + * + * Overwites an already existing chainable method + * and provides access to the previous function or + * property. Must return functions to be used for + * name. + * + * utils.overwriteChainableMethod(chai.Assertion.prototype, 'length', + * function (_super) { + * } + * , function (_super) { + * } + * ); + * + * Can also be accessed directly from `chai.Assertion`. + * + * chai.Assertion.overwriteChainableMethod('foo', fn, fn); + * + * Then can be used as any other assertion. + * + * expect(myFoo).to.have.length(3); + * expect(myFoo).to.have.length.above(3); + * + * @param {Object} ctx object whose method / property is to be overwritten + * @param {String} name of method / property to overwrite + * @param {Function} method function that returns a function to be used for name + * @param {Function} chainingBehavior function that returns a function to be used for property + * @name overwriteChainableMethod + * @api public + */ + +module.exports = function (ctx, name, method, chainingBehavior) { + var chainableBehavior = ctx.__methods[name]; + + var _chainingBehavior = chainableBehavior.chainingBehavior; + chainableBehavior.chainingBehavior = function () { + var result = chainingBehavior(_chainingBehavior).call(this); + return result === undefined ? this : result; + }; + + var _method = chainableBehavior.method; + chainableBehavior.method = function () { + var result = method(_method).apply(this, arguments); + return result === undefined ? this : result; + }; +}; + +}); +require.register("chai/lib/chai/utils/test.js", function(exports, require, module){ +/*! + * Chai - test utility + * Copyright(c) 2012-2014 Jake Luer + * MIT Licensed + */ + +/*! + * Module dependancies + */ + +var flag = require('./flag'); + +/** + * # test(object, expression) + * + * Test and object for expression. + * + * @param {Object} object (constructed Assertion) + * @param {Arguments} chai.Assertion.prototype.assert arguments + */ + +module.exports = function (obj, args) { + var negate = flag(obj, 'negate') + , expr = args[0]; + return negate ? !expr : expr; +}; + +}); +require.register("chai/lib/chai/utils/transferFlags.js", function(exports, require, module){ +/*! + * Chai - transferFlags utility + * Copyright(c) 2012-2014 Jake Luer + * MIT Licensed + */ + +/** + * ### transferFlags(assertion, object, includeAll = true) + * + * Transfer all the flags for `assertion` to `object`. If + * `includeAll` is set to `false`, then the base Chai + * assertion flags (namely `object`, `ssfi`, and `message`) + * will not be transferred. + * + * + * var newAssertion = new Assertion(); + * utils.transferFlags(assertion, newAssertion); + * + * var anotherAsseriton = new Assertion(myObj); + * utils.transferFlags(assertion, anotherAssertion, false); + * + * @param {Assertion} assertion the assertion to transfer the flags from + * @param {Object} object the object to transfer the flags too; usually a new assertion + * @param {Boolean} includeAll + * @name getAllFlags + * @api private + */ + +module.exports = function (assertion, object, includeAll) { + var flags = assertion.__flags || (assertion.__flags = Object.create(null)); + + if (!object.__flags) { + object.__flags = Object.create(null); + } + + includeAll = arguments.length === 3 ? includeAll : true; + + for (var flag in flags) { + if (includeAll || + (flag !== 'object' && flag !== 'ssfi' && flag != 'message')) { + object.__flags[flag] = flags[flag]; + } + } +}; + +}); +require.register("chai/lib/chai/utils/type.js", function(exports, require, module){ +/*! + * Chai - type utility + * Copyright(c) 2012-2014 Jake Luer + * MIT Licensed + */ + +/*! + * Detectable javascript natives + */ + +var natives = { + '[object Arguments]': 'arguments' + , '[object Array]': 'array' + , '[object Date]': 'date' + , '[object Function]': 'function' + , '[object Number]': 'number' + , '[object RegExp]': 'regexp' + , '[object String]': 'string' +}; + +/** + * ### type(object) + * + * Better implementation of `typeof` detection that can + * be used cross-browser. Handles the inconsistencies of + * Array, `null`, and `undefined` detection. + * + * utils.type({}) // 'object' + * utils.type(null) // `null' + * utils.type(undefined) // `undefined` + * utils.type([]) // `array` + * + * @param {Mixed} object to detect type of + * @name type + * @api private + */ + +module.exports = function (obj) { + var str = Object.prototype.toString.call(obj); + if (natives[str]) return natives[str]; + if (obj === null) return 'null'; + if (obj === undefined) return 'undefined'; + if (obj === Object(obj)) return 'object'; + return typeof obj; +}; + +}); + + + + +require.alias("chaijs-assertion-error/index.js", "chai/deps/assertion-error/index.js"); +require.alias("chaijs-assertion-error/index.js", "chai/deps/assertion-error/index.js"); +require.alias("chaijs-assertion-error/index.js", "assertion-error/index.js"); +require.alias("chaijs-assertion-error/index.js", "chaijs-assertion-error/index.js"); +require.alias("chaijs-deep-eql/lib/eql.js", "chai/deps/deep-eql/lib/eql.js"); +require.alias("chaijs-deep-eql/lib/eql.js", "chai/deps/deep-eql/index.js"); +require.alias("chaijs-deep-eql/lib/eql.js", "deep-eql/index.js"); +require.alias("chaijs-type-detect/lib/type.js", "chaijs-deep-eql/deps/type-detect/lib/type.js"); +require.alias("chaijs-type-detect/lib/type.js", "chaijs-deep-eql/deps/type-detect/index.js"); +require.alias("chaijs-type-detect/lib/type.js", "chaijs-type-detect/index.js"); +require.alias("chaijs-deep-eql/lib/eql.js", "chaijs-deep-eql/index.js"); +require.alias("chai/index.js", "chai/index.js");if (typeof exports == "object") { + module.exports = require("chai"); +} else if (typeof define == "function" && define.amd) { + define([], function(){ return require("chai"); }); +} else { + this["chai"] = require("chai"); +}})(); \ No newline at end of file diff --git a/vendor/assets/javascripts/class.js b/vendor/assets/javascripts/class.js index 63b570c369..afa6f1c8c6 100644 --- a/vendor/assets/javascripts/class.js +++ b/vendor/assets/javascripts/class.js @@ -61,4 +61,4 @@ return Class; }; -})(); +})(); \ No newline at end of file diff --git a/vendor/assets/javascripts/d3.js b/vendor/assets/javascripts/d3.js new file mode 100644 index 0000000000..a972735650 --- /dev/null +++ b/vendor/assets/javascripts/d3.js @@ -0,0 +1,9255 @@ +!function() { + var d3 = { + version: "3.4.8" + }; + if (!Date.now) Date.now = function() { + return +new Date(); + }; + var d3_arraySlice = [].slice, d3_array = function(list) { + return d3_arraySlice.call(list); + }; + var d3_document = document, d3_documentElement = d3_document.documentElement, d3_window = window; + try { + d3_array(d3_documentElement.childNodes)[0].nodeType; + } catch (e) { + d3_array = function(list) { + var i = list.length, array = new Array(i); + while (i--) array[i] = list[i]; + return array; + }; + } + try { + d3_document.createElement("div").style.setProperty("opacity", 0, ""); + } catch (error) { + var d3_element_prototype = d3_window.Element.prototype, d3_element_setAttribute = d3_element_prototype.setAttribute, d3_element_setAttributeNS = d3_element_prototype.setAttributeNS, d3_style_prototype = d3_window.CSSStyleDeclaration.prototype, d3_style_setProperty = d3_style_prototype.setProperty; + d3_element_prototype.setAttribute = function(name, value) { + d3_element_setAttribute.call(this, name, value + ""); + }; + d3_element_prototype.setAttributeNS = function(space, local, value) { + d3_element_setAttributeNS.call(this, space, local, value + ""); + }; + d3_style_prototype.setProperty = function(name, value, priority) { + d3_style_setProperty.call(this, name, value + "", priority); + }; + } + d3.ascending = d3_ascending; + function d3_ascending(a, b) { + return a < b ? -1 : a > b ? 1 : a >= b ? 0 : NaN; + } + d3.descending = function(a, b) { + return b < a ? -1 : b > a ? 1 : b >= a ? 0 : NaN; + }; + d3.min = function(array, f) { + var i = -1, n = array.length, a, b; + if (arguments.length === 1) { + while (++i < n && !((a = array[i]) != null && a <= a)) a = undefined; + while (++i < n) if ((b = array[i]) != null && a > b) a = b; + } else { + while (++i < n && !((a = f.call(array, array[i], i)) != null && a <= a)) a = undefined; + while (++i < n) if ((b = f.call(array, array[i], i)) != null && a > b) a = b; + } + return a; + }; + d3.max = function(array, f) { + var i = -1, n = array.length, a, b; + if (arguments.length === 1) { + while (++i < n && !((a = array[i]) != null && a <= a)) a = undefined; + while (++i < n) if ((b = array[i]) != null && b > a) a = b; + } else { + while (++i < n && !((a = f.call(array, array[i], i)) != null && a <= a)) a = undefined; + while (++i < n) if ((b = f.call(array, array[i], i)) != null && b > a) a = b; + } + return a; + }; + d3.extent = function(array, f) { + var i = -1, n = array.length, a, b, c; + if (arguments.length === 1) { + while (++i < n && !((a = c = array[i]) != null && a <= a)) a = c = undefined; + while (++i < n) if ((b = array[i]) != null) { + if (a > b) a = b; + if (c < b) c = b; + } + } else { + while (++i < n && !((a = c = f.call(array, array[i], i)) != null && a <= a)) a = undefined; + while (++i < n) if ((b = f.call(array, array[i], i)) != null) { + if (a > b) a = b; + if (c < b) c = b; + } + } + return [ a, c ]; + }; + d3.sum = function(array, f) { + var s = 0, n = array.length, a, i = -1; + if (arguments.length === 1) { + while (++i < n) if (!isNaN(a = +array[i])) s += a; + } else { + while (++i < n) if (!isNaN(a = +f.call(array, array[i], i))) s += a; + } + return s; + }; + function d3_number(x) { + return x != null && !isNaN(x); + } + d3.mean = function(array, f) { + var s = 0, n = array.length, a, i = -1, j = n; + if (arguments.length === 1) { + while (++i < n) if (d3_number(a = array[i])) s += a; else --j; + } else { + while (++i < n) if (d3_number(a = f.call(array, array[i], i))) s += a; else --j; + } + return j ? s / j : undefined; + }; + d3.quantile = function(values, p) { + var H = (values.length - 1) * p + 1, h = Math.floor(H), v = +values[h - 1], e = H - h; + return e ? v + e * (values[h] - v) : v; + }; + d3.median = function(array, f) { + if (arguments.length > 1) array = array.map(f); + array = array.filter(d3_number); + return array.length ? d3.quantile(array.sort(d3_ascending), .5) : undefined; + }; + function d3_bisector(compare) { + return { + left: function(a, x, lo, hi) { + if (arguments.length < 3) lo = 0; + if (arguments.length < 4) hi = a.length; + while (lo < hi) { + var mid = lo + hi >>> 1; + if (compare(a[mid], x) < 0) lo = mid + 1; else hi = mid; + } + return lo; + }, + right: function(a, x, lo, hi) { + if (arguments.length < 3) lo = 0; + if (arguments.length < 4) hi = a.length; + while (lo < hi) { + var mid = lo + hi >>> 1; + if (compare(a[mid], x) > 0) hi = mid; else lo = mid + 1; + } + return lo; + } + }; + } + var d3_bisect = d3_bisector(d3_ascending); + d3.bisectLeft = d3_bisect.left; + d3.bisect = d3.bisectRight = d3_bisect.right; + d3.bisector = function(f) { + return d3_bisector(f.length === 1 ? function(d, x) { + return d3_ascending(f(d), x); + } : f); + }; + d3.shuffle = function(array) { + var m = array.length, t, i; + while (m) { + i = Math.random() * m-- | 0; + t = array[m], array[m] = array[i], array[i] = t; + } + return array; + }; + d3.permute = function(array, indexes) { + var i = indexes.length, permutes = new Array(i); + while (i--) permutes[i] = array[indexes[i]]; + return permutes; + }; + d3.pairs = function(array) { + var i = 0, n = array.length - 1, p0, p1 = array[0], pairs = new Array(n < 0 ? 0 : n); + while (i < n) pairs[i] = [ p0 = p1, p1 = array[++i] ]; + return pairs; + }; + d3.zip = function() { + if (!(n = arguments.length)) return []; + for (var i = -1, m = d3.min(arguments, d3_zipLength), zips = new Array(m); ++i < m; ) { + for (var j = -1, n, zip = zips[i] = new Array(n); ++j < n; ) { + zip[j] = arguments[j][i]; + } + } + return zips; + }; + function d3_zipLength(d) { + return d.length; + } + d3.transpose = function(matrix) { + return d3.zip.apply(d3, matrix); + }; + d3.keys = function(map) { + var keys = []; + for (var key in map) keys.push(key); + return keys; + }; + d3.values = function(map) { + var values = []; + for (var key in map) values.push(map[key]); + return values; + }; + d3.entries = function(map) { + var entries = []; + for (var key in map) entries.push({ + key: key, + value: map[key] + }); + return entries; + }; + d3.merge = function(arrays) { + var n = arrays.length, m, i = -1, j = 0, merged, array; + while (++i < n) j += arrays[i].length; + merged = new Array(j); + while (--n >= 0) { + array = arrays[n]; + m = array.length; + while (--m >= 0) { + merged[--j] = array[m]; + } + } + return merged; + }; + var abs = Math.abs; + d3.range = function(start, stop, step) { + if (arguments.length < 3) { + step = 1; + if (arguments.length < 2) { + stop = start; + start = 0; + } + } + if ((stop - start) / step === Infinity) throw new Error("infinite range"); + var range = [], k = d3_range_integerScale(abs(step)), i = -1, j; + start *= k, stop *= k, step *= k; + if (step < 0) while ((j = start + step * ++i) > stop) range.push(j / k); else while ((j = start + step * ++i) < stop) range.push(j / k); + return range; + }; + function d3_range_integerScale(x) { + var k = 1; + while (x * k % 1) k *= 10; + return k; + } + function d3_class(ctor, properties) { + try { + for (var key in properties) { + Object.defineProperty(ctor.prototype, key, { + value: properties[key], + enumerable: false + }); + } + } catch (e) { + ctor.prototype = properties; + } + } + d3.map = function(object) { + var map = new d3_Map(); + if (object instanceof d3_Map) object.forEach(function(key, value) { + map.set(key, value); + }); else for (var key in object) map.set(key, object[key]); + return map; + }; + function d3_Map() {} + d3_class(d3_Map, { + has: d3_map_has, + get: function(key) { + return this[d3_map_prefix + key]; + }, + set: function(key, value) { + return this[d3_map_prefix + key] = value; + }, + remove: d3_map_remove, + keys: d3_map_keys, + values: function() { + var values = []; + this.forEach(function(key, value) { + values.push(value); + }); + return values; + }, + entries: function() { + var entries = []; + this.forEach(function(key, value) { + entries.push({ + key: key, + value: value + }); + }); + return entries; + }, + size: d3_map_size, + empty: d3_map_empty, + forEach: function(f) { + for (var key in this) if (key.charCodeAt(0) === d3_map_prefixCode) f.call(this, key.substring(1), this[key]); + } + }); + var d3_map_prefix = "\x00", d3_map_prefixCode = d3_map_prefix.charCodeAt(0); + function d3_map_has(key) { + return d3_map_prefix + key in this; + } + function d3_map_remove(key) { + key = d3_map_prefix + key; + return key in this && delete this[key]; + } + function d3_map_keys() { + var keys = []; + this.forEach(function(key) { + keys.push(key); + }); + return keys; + } + function d3_map_size() { + var size = 0; + for (var key in this) if (key.charCodeAt(0) === d3_map_prefixCode) ++size; + return size; + } + function d3_map_empty() { + for (var key in this) if (key.charCodeAt(0) === d3_map_prefixCode) return false; + return true; + } + d3.nest = function() { + var nest = {}, keys = [], sortKeys = [], sortValues, rollup; + function map(mapType, array, depth) { + if (depth >= keys.length) return rollup ? rollup.call(nest, array) : sortValues ? array.sort(sortValues) : array; + var i = -1, n = array.length, key = keys[depth++], keyValue, object, setter, valuesByKey = new d3_Map(), values; + while (++i < n) { + if (values = valuesByKey.get(keyValue = key(object = array[i]))) { + values.push(object); + } else { + valuesByKey.set(keyValue, [ object ]); + } + } + if (mapType) { + object = mapType(); + setter = function(keyValue, values) { + object.set(keyValue, map(mapType, values, depth)); + }; + } else { + object = {}; + setter = function(keyValue, values) { + object[keyValue] = map(mapType, values, depth); + }; + } + valuesByKey.forEach(setter); + return object; + } + function entries(map, depth) { + if (depth >= keys.length) return map; + var array = [], sortKey = sortKeys[depth++]; + map.forEach(function(key, keyMap) { + array.push({ + key: key, + values: entries(keyMap, depth) + }); + }); + return sortKey ? array.sort(function(a, b) { + return sortKey(a.key, b.key); + }) : array; + } + nest.map = function(array, mapType) { + return map(mapType, array, 0); + }; + nest.entries = function(array) { + return entries(map(d3.map, array, 0), 0); + }; + nest.key = function(d) { + keys.push(d); + return nest; + }; + nest.sortKeys = function(order) { + sortKeys[keys.length - 1] = order; + return nest; + }; + nest.sortValues = function(order) { + sortValues = order; + return nest; + }; + nest.rollup = function(f) { + rollup = f; + return nest; + }; + return nest; + }; + d3.set = function(array) { + var set = new d3_Set(); + if (array) for (var i = 0, n = array.length; i < n; ++i) set.add(array[i]); + return set; + }; + function d3_Set() {} + d3_class(d3_Set, { + has: d3_map_has, + add: function(value) { + this[d3_map_prefix + value] = true; + return value; + }, + remove: function(value) { + value = d3_map_prefix + value; + return value in this && delete this[value]; + }, + values: d3_map_keys, + size: d3_map_size, + empty: d3_map_empty, + forEach: function(f) { + for (var value in this) if (value.charCodeAt(0) === d3_map_prefixCode) f.call(this, value.substring(1)); + } + }); + d3.behavior = {}; + d3.rebind = function(target, source) { + var i = 1, n = arguments.length, method; + while (++i < n) target[method = arguments[i]] = d3_rebind(target, source, source[method]); + return target; + }; + function d3_rebind(target, source, method) { + return function() { + var value = method.apply(source, arguments); + return value === source ? target : value; + }; + } + function d3_vendorSymbol(object, name) { + if (name in object) return name; + name = name.charAt(0).toUpperCase() + name.substring(1); + for (var i = 0, n = d3_vendorPrefixes.length; i < n; ++i) { + var prefixName = d3_vendorPrefixes[i] + name; + if (prefixName in object) return prefixName; + } + } + var d3_vendorPrefixes = [ "webkit", "ms", "moz", "Moz", "o", "O" ]; + function d3_noop() {} + d3.dispatch = function() { + var dispatch = new d3_dispatch(), i = -1, n = arguments.length; + while (++i < n) dispatch[arguments[i]] = d3_dispatch_event(dispatch); + return dispatch; + }; + function d3_dispatch() {} + d3_dispatch.prototype.on = function(type, listener) { + var i = type.indexOf("."), name = ""; + if (i >= 0) { + name = type.substring(i + 1); + type = type.substring(0, i); + } + if (type) return arguments.length < 2 ? this[type].on(name) : this[type].on(name, listener); + if (arguments.length === 2) { + if (listener == null) for (type in this) { + if (this.hasOwnProperty(type)) this[type].on(name, null); + } + return this; + } + }; + function d3_dispatch_event(dispatch) { + var listeners = [], listenerByName = new d3_Map(); + function event() { + var z = listeners, i = -1, n = z.length, l; + while (++i < n) if (l = z[i].on) l.apply(this, arguments); + return dispatch; + } + event.on = function(name, listener) { + var l = listenerByName.get(name), i; + if (arguments.length < 2) return l && l.on; + if (l) { + l.on = null; + listeners = listeners.slice(0, i = listeners.indexOf(l)).concat(listeners.slice(i + 1)); + listenerByName.remove(name); + } + if (listener) listeners.push(listenerByName.set(name, { + on: listener + })); + return dispatch; + }; + return event; + } + d3.event = null; + function d3_eventPreventDefault() { + d3.event.preventDefault(); + } + function d3_eventSource() { + var e = d3.event, s; + while (s = e.sourceEvent) e = s; + return e; + } + function d3_eventDispatch(target) { + var dispatch = new d3_dispatch(), i = 0, n = arguments.length; + while (++i < n) dispatch[arguments[i]] = d3_dispatch_event(dispatch); + dispatch.of = function(thiz, argumentz) { + return function(e1) { + try { + var e0 = e1.sourceEvent = d3.event; + e1.target = target; + d3.event = e1; + dispatch[e1.type].apply(thiz, argumentz); + } finally { + d3.event = e0; + } + }; + }; + return dispatch; + } + d3.requote = function(s) { + return s.replace(d3_requote_re, "\\$&"); + }; + var d3_requote_re = /[\\\^\$\*\+\?\|\[\]\(\)\.\{\}]/g; + var d3_subclass = {}.__proto__ ? function(object, prototype) { + object.__proto__ = prototype; + } : function(object, prototype) { + for (var property in prototype) object[property] = prototype[property]; + }; + function d3_selection(groups) { + d3_subclass(groups, d3_selectionPrototype); + return groups; + } + var d3_select = function(s, n) { + return n.querySelector(s); + }, d3_selectAll = function(s, n) { + return n.querySelectorAll(s); + }, d3_selectMatcher = d3_documentElement[d3_vendorSymbol(d3_documentElement, "matchesSelector")], d3_selectMatches = function(n, s) { + return d3_selectMatcher.call(n, s); + }; + if (typeof Sizzle === "function") { + d3_select = function(s, n) { + return Sizzle(s, n)[0] || null; + }; + d3_selectAll = Sizzle; + d3_selectMatches = Sizzle.matchesSelector; + } + d3.selection = function() { + return d3_selectionRoot; + }; + var d3_selectionPrototype = d3.selection.prototype = []; + d3_selectionPrototype.select = function(selector) { + var subgroups = [], subgroup, subnode, group, node; + selector = d3_selection_selector(selector); + for (var j = -1, m = this.length; ++j < m; ) { + subgroups.push(subgroup = []); + subgroup.parentNode = (group = this[j]).parentNode; + for (var i = -1, n = group.length; ++i < n; ) { + if (node = group[i]) { + subgroup.push(subnode = selector.call(node, node.__data__, i, j)); + if (subnode && "__data__" in node) subnode.__data__ = node.__data__; + } else { + subgroup.push(null); + } + } + } + return d3_selection(subgroups); + }; + function d3_selection_selector(selector) { + return typeof selector === "function" ? selector : function() { + return d3_select(selector, this); + }; + } + d3_selectionPrototype.selectAll = function(selector) { + var subgroups = [], subgroup, node; + selector = d3_selection_selectorAll(selector); + for (var j = -1, m = this.length; ++j < m; ) { + for (var group = this[j], i = -1, n = group.length; ++i < n; ) { + if (node = group[i]) { + subgroups.push(subgroup = d3_array(selector.call(node, node.__data__, i, j))); + subgroup.parentNode = node; + } + } + } + return d3_selection(subgroups); + }; + function d3_selection_selectorAll(selector) { + return typeof selector === "function" ? selector : function() { + return d3_selectAll(selector, this); + }; + } + var d3_nsPrefix = { + svg: "http://www.w3.org/2000/svg", + xhtml: "http://www.w3.org/1999/xhtml", + xlink: "http://www.w3.org/1999/xlink", + xml: "http://www.w3.org/XML/1998/namespace", + xmlns: "http://www.w3.org/2000/xmlns/" + }; + d3.ns = { + prefix: d3_nsPrefix, + qualify: function(name) { + var i = name.indexOf(":"), prefix = name; + if (i >= 0) { + prefix = name.substring(0, i); + name = name.substring(i + 1); + } + return d3_nsPrefix.hasOwnProperty(prefix) ? { + space: d3_nsPrefix[prefix], + local: name + } : name; + } + }; + d3_selectionPrototype.attr = function(name, value) { + if (arguments.length < 2) { + if (typeof name === "string") { + var node = this.node(); + name = d3.ns.qualify(name); + return name.local ? node.getAttributeNS(name.space, name.local) : node.getAttribute(name); + } + for (value in name) this.each(d3_selection_attr(value, name[value])); + return this; + } + return this.each(d3_selection_attr(name, value)); + }; + function d3_selection_attr(name, value) { + name = d3.ns.qualify(name); + function attrNull() { + this.removeAttribute(name); + } + function attrNullNS() { + this.removeAttributeNS(name.space, name.local); + } + function attrConstant() { + this.setAttribute(name, value); + } + function attrConstantNS() { + this.setAttributeNS(name.space, name.local, value); + } + function attrFunction() { + var x = value.apply(this, arguments); + if (x == null) this.removeAttribute(name); else this.setAttribute(name, x); + } + function attrFunctionNS() { + var x = value.apply(this, arguments); + if (x == null) this.removeAttributeNS(name.space, name.local); else this.setAttributeNS(name.space, name.local, x); + } + return value == null ? name.local ? attrNullNS : attrNull : typeof value === "function" ? name.local ? attrFunctionNS : attrFunction : name.local ? attrConstantNS : attrConstant; + } + function d3_collapse(s) { + return s.trim().replace(/\s+/g, " "); + } + d3_selectionPrototype.classed = function(name, value) { + if (arguments.length < 2) { + if (typeof name === "string") { + var node = this.node(), n = (name = d3_selection_classes(name)).length, i = -1; + if (value = node.classList) { + while (++i < n) if (!value.contains(name[i])) return false; + } else { + value = node.getAttribute("class"); + while (++i < n) if (!d3_selection_classedRe(name[i]).test(value)) return false; + } + return true; + } + for (value in name) this.each(d3_selection_classed(value, name[value])); + return this; + } + return this.each(d3_selection_classed(name, value)); + }; + function d3_selection_classedRe(name) { + return new RegExp("(?:^|\\s+)" + d3.requote(name) + "(?:\\s+|$)", "g"); + } + function d3_selection_classes(name) { + return name.trim().split(/^|\s+/); + } + function d3_selection_classed(name, value) { + name = d3_selection_classes(name).map(d3_selection_classedName); + var n = name.length; + function classedConstant() { + var i = -1; + while (++i < n) name[i](this, value); + } + function classedFunction() { + var i = -1, x = value.apply(this, arguments); + while (++i < n) name[i](this, x); + } + return typeof value === "function" ? classedFunction : classedConstant; + } + function d3_selection_classedName(name) { + var re = d3_selection_classedRe(name); + return function(node, value) { + if (c = node.classList) return value ? c.add(name) : c.remove(name); + var c = node.getAttribute("class") || ""; + if (value) { + re.lastIndex = 0; + if (!re.test(c)) node.setAttribute("class", d3_collapse(c + " " + name)); + } else { + node.setAttribute("class", d3_collapse(c.replace(re, " "))); + } + }; + } + d3_selectionPrototype.style = function(name, value, priority) { + var n = arguments.length; + if (n < 3) { + if (typeof name !== "string") { + if (n < 2) value = ""; + for (priority in name) this.each(d3_selection_style(priority, name[priority], value)); + return this; + } + if (n < 2) return d3_window.getComputedStyle(this.node(), null).getPropertyValue(name); + priority = ""; + } + return this.each(d3_selection_style(name, value, priority)); + }; + function d3_selection_style(name, value, priority) { + function styleNull() { + this.style.removeProperty(name); + } + function styleConstant() { + this.style.setProperty(name, value, priority); + } + function styleFunction() { + var x = value.apply(this, arguments); + if (x == null) this.style.removeProperty(name); else this.style.setProperty(name, x, priority); + } + return value == null ? styleNull : typeof value === "function" ? styleFunction : styleConstant; + } + d3_selectionPrototype.property = function(name, value) { + if (arguments.length < 2) { + if (typeof name === "string") return this.node()[name]; + for (value in name) this.each(d3_selection_property(value, name[value])); + return this; + } + return this.each(d3_selection_property(name, value)); + }; + function d3_selection_property(name, value) { + function propertyNull() { + delete this[name]; + } + function propertyConstant() { + this[name] = value; + } + function propertyFunction() { + var x = value.apply(this, arguments); + if (x == null) delete this[name]; else this[name] = x; + } + return value == null ? propertyNull : typeof value === "function" ? propertyFunction : propertyConstant; + } + d3_selectionPrototype.text = function(value) { + return arguments.length ? this.each(typeof value === "function" ? function() { + var v = value.apply(this, arguments); + this.textContent = v == null ? "" : v; + } : value == null ? function() { + this.textContent = ""; + } : function() { + this.textContent = value; + }) : this.node().textContent; + }; + d3_selectionPrototype.html = function(value) { + return arguments.length ? this.each(typeof value === "function" ? function() { + var v = value.apply(this, arguments); + this.innerHTML = v == null ? "" : v; + } : value == null ? function() { + this.innerHTML = ""; + } : function() { + this.innerHTML = value; + }) : this.node().innerHTML; + }; + d3_selectionPrototype.append = function(name) { + name = d3_selection_creator(name); + return this.select(function() { + return this.appendChild(name.apply(this, arguments)); + }); + }; + function d3_selection_creator(name) { + return typeof name === "function" ? name : (name = d3.ns.qualify(name)).local ? function() { + return this.ownerDocument.createElementNS(name.space, name.local); + } : function() { + return this.ownerDocument.createElementNS(this.namespaceURI, name); + }; + } + d3_selectionPrototype.insert = function(name, before) { + name = d3_selection_creator(name); + before = d3_selection_selector(before); + return this.select(function() { + return this.insertBefore(name.apply(this, arguments), before.apply(this, arguments) || null); + }); + }; + d3_selectionPrototype.remove = function() { + return this.each(function() { + var parent = this.parentNode; + if (parent) parent.removeChild(this); + }); + }; + d3_selectionPrototype.data = function(value, key) { + var i = -1, n = this.length, group, node; + if (!arguments.length) { + value = new Array(n = (group = this[0]).length); + while (++i < n) { + if (node = group[i]) { + value[i] = node.__data__; + } + } + return value; + } + function bind(group, groupData) { + var i, n = group.length, m = groupData.length, n0 = Math.min(n, m), updateNodes = new Array(m), enterNodes = new Array(m), exitNodes = new Array(n), node, nodeData; + if (key) { + var nodeByKeyValue = new d3_Map(), dataByKeyValue = new d3_Map(), keyValues = [], keyValue; + for (i = -1; ++i < n; ) { + keyValue = key.call(node = group[i], node.__data__, i); + if (nodeByKeyValue.has(keyValue)) { + exitNodes[i] = node; + } else { + nodeByKeyValue.set(keyValue, node); + } + keyValues.push(keyValue); + } + for (i = -1; ++i < m; ) { + keyValue = key.call(groupData, nodeData = groupData[i], i); + if (node = nodeByKeyValue.get(keyValue)) { + updateNodes[i] = node; + node.__data__ = nodeData; + } else if (!dataByKeyValue.has(keyValue)) { + enterNodes[i] = d3_selection_dataNode(nodeData); + } + dataByKeyValue.set(keyValue, nodeData); + nodeByKeyValue.remove(keyValue); + } + for (i = -1; ++i < n; ) { + if (nodeByKeyValue.has(keyValues[i])) { + exitNodes[i] = group[i]; + } + } + } else { + for (i = -1; ++i < n0; ) { + node = group[i]; + nodeData = groupData[i]; + if (node) { + node.__data__ = nodeData; + updateNodes[i] = node; + } else { + enterNodes[i] = d3_selection_dataNode(nodeData); + } + } + for (;i < m; ++i) { + enterNodes[i] = d3_selection_dataNode(groupData[i]); + } + for (;i < n; ++i) { + exitNodes[i] = group[i]; + } + } + enterNodes.update = updateNodes; + enterNodes.parentNode = updateNodes.parentNode = exitNodes.parentNode = group.parentNode; + enter.push(enterNodes); + update.push(updateNodes); + exit.push(exitNodes); + } + var enter = d3_selection_enter([]), update = d3_selection([]), exit = d3_selection([]); + if (typeof value === "function") { + while (++i < n) { + bind(group = this[i], value.call(group, group.parentNode.__data__, i)); + } + } else { + while (++i < n) { + bind(group = this[i], value); + } + } + update.enter = function() { + return enter; + }; + update.exit = function() { + return exit; + }; + return update; + }; + function d3_selection_dataNode(data) { + return { + __data__: data + }; + } + d3_selectionPrototype.datum = function(value) { + return arguments.length ? this.property("__data__", value) : this.property("__data__"); + }; + d3_selectionPrototype.filter = function(filter) { + var subgroups = [], subgroup, group, node; + if (typeof filter !== "function") filter = d3_selection_filter(filter); + for (var j = 0, m = this.length; j < m; j++) { + subgroups.push(subgroup = []); + subgroup.parentNode = (group = this[j]).parentNode; + for (var i = 0, n = group.length; i < n; i++) { + if ((node = group[i]) && filter.call(node, node.__data__, i, j)) { + subgroup.push(node); + } + } + } + return d3_selection(subgroups); + }; + function d3_selection_filter(selector) { + return function() { + return d3_selectMatches(this, selector); + }; + } + d3_selectionPrototype.order = function() { + for (var j = -1, m = this.length; ++j < m; ) { + for (var group = this[j], i = group.length - 1, next = group[i], node; --i >= 0; ) { + if (node = group[i]) { + if (next && next !== node.nextSibling) next.parentNode.insertBefore(node, next); + next = node; + } + } + } + return this; + }; + d3_selectionPrototype.sort = function(comparator) { + comparator = d3_selection_sortComparator.apply(this, arguments); + for (var j = -1, m = this.length; ++j < m; ) this[j].sort(comparator); + return this.order(); + }; + function d3_selection_sortComparator(comparator) { + if (!arguments.length) comparator = d3_ascending; + return function(a, b) { + return a && b ? comparator(a.__data__, b.__data__) : !a - !b; + }; + } + d3_selectionPrototype.each = function(callback) { + return d3_selection_each(this, function(node, i, j) { + callback.call(node, node.__data__, i, j); + }); + }; + function d3_selection_each(groups, callback) { + for (var j = 0, m = groups.length; j < m; j++) { + for (var group = groups[j], i = 0, n = group.length, node; i < n; i++) { + if (node = group[i]) callback(node, i, j); + } + } + return groups; + } + d3_selectionPrototype.call = function(callback) { + var args = d3_array(arguments); + callback.apply(args[0] = this, args); + return this; + }; + d3_selectionPrototype.empty = function() { + return !this.node(); + }; + d3_selectionPrototype.node = function() { + for (var j = 0, m = this.length; j < m; j++) { + for (var group = this[j], i = 0, n = group.length; i < n; i++) { + var node = group[i]; + if (node) return node; + } + } + return null; + }; + d3_selectionPrototype.size = function() { + var n = 0; + this.each(function() { + ++n; + }); + return n; + }; + function d3_selection_enter(selection) { + d3_subclass(selection, d3_selection_enterPrototype); + return selection; + } + var d3_selection_enterPrototype = []; + d3.selection.enter = d3_selection_enter; + d3.selection.enter.prototype = d3_selection_enterPrototype; + d3_selection_enterPrototype.append = d3_selectionPrototype.append; + d3_selection_enterPrototype.empty = d3_selectionPrototype.empty; + d3_selection_enterPrototype.node = d3_selectionPrototype.node; + d3_selection_enterPrototype.call = d3_selectionPrototype.call; + d3_selection_enterPrototype.size = d3_selectionPrototype.size; + d3_selection_enterPrototype.select = function(selector) { + var subgroups = [], subgroup, subnode, upgroup, group, node; + for (var j = -1, m = this.length; ++j < m; ) { + upgroup = (group = this[j]).update; + subgroups.push(subgroup = []); + subgroup.parentNode = group.parentNode; + for (var i = -1, n = group.length; ++i < n; ) { + if (node = group[i]) { + subgroup.push(upgroup[i] = subnode = selector.call(group.parentNode, node.__data__, i, j)); + subnode.__data__ = node.__data__; + } else { + subgroup.push(null); + } + } + } + return d3_selection(subgroups); + }; + d3_selection_enterPrototype.insert = function(name, before) { + if (arguments.length < 2) before = d3_selection_enterInsertBefore(this); + return d3_selectionPrototype.insert.call(this, name, before); + }; + function d3_selection_enterInsertBefore(enter) { + var i0, j0; + return function(d, i, j) { + var group = enter[j].update, n = group.length, node; + if (j != j0) j0 = j, i0 = 0; + if (i >= i0) i0 = i + 1; + while (!(node = group[i0]) && ++i0 < n) ; + return node; + }; + } + d3_selectionPrototype.transition = function() { + var id = d3_transitionInheritId || ++d3_transitionId, subgroups = [], subgroup, node, transition = d3_transitionInherit || { + time: Date.now(), + ease: d3_ease_cubicInOut, + delay: 0, + duration: 250 + }; + for (var j = -1, m = this.length; ++j < m; ) { + subgroups.push(subgroup = []); + for (var group = this[j], i = -1, n = group.length; ++i < n; ) { + if (node = group[i]) d3_transitionNode(node, i, id, transition); + subgroup.push(node); + } + } + return d3_transition(subgroups, id); + }; + d3_selectionPrototype.interrupt = function() { + return this.each(d3_selection_interrupt); + }; + function d3_selection_interrupt() { + var lock = this.__transition__; + if (lock) ++lock.active; + } + d3.select = function(node) { + var group = [ typeof node === "string" ? d3_select(node, d3_document) : node ]; + group.parentNode = d3_documentElement; + return d3_selection([ group ]); + }; + d3.selectAll = function(nodes) { + var group = d3_array(typeof nodes === "string" ? d3_selectAll(nodes, d3_document) : nodes); + group.parentNode = d3_documentElement; + return d3_selection([ group ]); + }; + var d3_selectionRoot = d3.select(d3_documentElement); + d3_selectionPrototype.on = function(type, listener, capture) { + var n = arguments.length; + if (n < 3) { + if (typeof type !== "string") { + if (n < 2) listener = false; + for (capture in type) this.each(d3_selection_on(capture, type[capture], listener)); + return this; + } + if (n < 2) return (n = this.node()["__on" + type]) && n._; + capture = false; + } + return this.each(d3_selection_on(type, listener, capture)); + }; + function d3_selection_on(type, listener, capture) { + var name = "__on" + type, i = type.indexOf("."), wrap = d3_selection_onListener; + if (i > 0) type = type.substring(0, i); + var filter = d3_selection_onFilters.get(type); + if (filter) type = filter, wrap = d3_selection_onFilter; + function onRemove() { + var l = this[name]; + if (l) { + this.removeEventListener(type, l, l.$); + delete this[name]; + } + } + function onAdd() { + var l = wrap(listener, d3_array(arguments)); + onRemove.call(this); + this.addEventListener(type, this[name] = l, l.$ = capture); + l._ = listener; + } + function removeAll() { + var re = new RegExp("^__on([^.]+)" + d3.requote(type) + "$"), match; + for (var name in this) { + if (match = name.match(re)) { + var l = this[name]; + this.removeEventListener(match[1], l, l.$); + delete this[name]; + } + } + } + return i ? listener ? onAdd : onRemove : listener ? d3_noop : removeAll; + } + var d3_selection_onFilters = d3.map({ + mouseenter: "mouseover", + mouseleave: "mouseout" + }); + d3_selection_onFilters.forEach(function(k) { + if ("on" + k in d3_document) d3_selection_onFilters.remove(k); + }); + function d3_selection_onListener(listener, argumentz) { + return function(e) { + var o = d3.event; + d3.event = e; + argumentz[0] = this.__data__; + try { + listener.apply(this, argumentz); + } finally { + d3.event = o; + } + }; + } + function d3_selection_onFilter(listener, argumentz) { + var l = d3_selection_onListener(listener, argumentz); + return function(e) { + var target = this, related = e.relatedTarget; + if (!related || related !== target && !(related.compareDocumentPosition(target) & 8)) { + l.call(target, e); + } + }; + } + var d3_event_dragSelect = "onselectstart" in d3_document ? null : d3_vendorSymbol(d3_documentElement.style, "userSelect"), d3_event_dragId = 0; + function d3_event_dragSuppress() { + var name = ".dragsuppress-" + ++d3_event_dragId, click = "click" + name, w = d3.select(d3_window).on("touchmove" + name, d3_eventPreventDefault).on("dragstart" + name, d3_eventPreventDefault).on("selectstart" + name, d3_eventPreventDefault); + if (d3_event_dragSelect) { + var style = d3_documentElement.style, select = style[d3_event_dragSelect]; + style[d3_event_dragSelect] = "none"; + } + return function(suppressClick) { + w.on(name, null); + if (d3_event_dragSelect) style[d3_event_dragSelect] = select; + if (suppressClick) { + function off() { + w.on(click, null); + } + w.on(click, function() { + d3_eventPreventDefault(); + off(); + }, true); + setTimeout(off, 0); + } + }; + } + d3.mouse = function(container) { + return d3_mousePoint(container, d3_eventSource()); + }; + function d3_mousePoint(container, e) { + if (e.changedTouches) e = e.changedTouches[0]; + var svg = container.ownerSVGElement || container; + if (svg.createSVGPoint) { + var point = svg.createSVGPoint(); + point.x = e.clientX, point.y = e.clientY; + point = point.matrixTransform(container.getScreenCTM().inverse()); + return [ point.x, point.y ]; + } + var rect = container.getBoundingClientRect(); + return [ e.clientX - rect.left - container.clientLeft, e.clientY - rect.top - container.clientTop ]; + } + d3.touches = function(container, touches) { + if (arguments.length < 2) touches = d3_eventSource().touches; + return touches ? d3_array(touches).map(function(touch) { + var point = d3_mousePoint(container, touch); + point.identifier = touch.identifier; + return point; + }) : []; + }; + d3.behavior.drag = function() { + var event = d3_eventDispatch(drag, "drag", "dragstart", "dragend"), origin = null, mousedown = dragstart(d3_noop, d3.mouse, d3_behavior_dragMouseSubject, "mousemove", "mouseup"), touchstart = dragstart(d3_behavior_dragTouchId, d3.touch, d3_behavior_dragTouchSubject, "touchmove", "touchend"); + function drag() { + this.on("mousedown.drag", mousedown).on("touchstart.drag", touchstart); + } + function dragstart(id, position, subject, move, end) { + return function() { + var that = this, target = d3.event.target, parent = that.parentNode, dispatch = event.of(that, arguments), dragged = 0, dragId = id(), dragName = ".drag" + (dragId == null ? "" : "-" + dragId), dragOffset, dragSubject = d3.select(subject()).on(move + dragName, moved).on(end + dragName, ended), dragRestore = d3_event_dragSuppress(), position0 = position(parent, dragId); + if (origin) { + dragOffset = origin.apply(that, arguments); + dragOffset = [ dragOffset.x - position0[0], dragOffset.y - position0[1] ]; + } else { + dragOffset = [ 0, 0 ]; + } + dispatch({ + type: "dragstart" + }); + function moved() { + var position1 = position(parent, dragId), dx, dy; + if (!position1) return; + dx = position1[0] - position0[0]; + dy = position1[1] - position0[1]; + dragged |= dx | dy; + position0 = position1; + dispatch({ + type: "drag", + x: position1[0] + dragOffset[0], + y: position1[1] + dragOffset[1], + dx: dx, + dy: dy + }); + } + function ended() { + if (!position(parent, dragId)) return; + dragSubject.on(move + dragName, null).on(end + dragName, null); + dragRestore(dragged && d3.event.target === target); + dispatch({ + type: "dragend" + }); + } + }; + } + drag.origin = function(x) { + if (!arguments.length) return origin; + origin = x; + return drag; + }; + return d3.rebind(drag, event, "on"); + }; + function d3_behavior_dragTouchId() { + return d3.event.changedTouches[0].identifier; + } + function d3_behavior_dragTouchSubject() { + return d3.event.target; + } + function d3_behavior_dragMouseSubject() { + return d3_window; + } + var π = Math.PI, τ = 2 * π, halfπ = π / 2, ε = 1e-6, ε2 = ε * ε, d3_radians = π / 180, d3_degrees = 180 / π; + function d3_sgn(x) { + return x > 0 ? 1 : x < 0 ? -1 : 0; + } + function d3_cross2d(a, b, c) { + return (b[0] - a[0]) * (c[1] - a[1]) - (b[1] - a[1]) * (c[0] - a[0]); + } + function d3_acos(x) { + return x > 1 ? 0 : x < -1 ? π : Math.acos(x); + } + function d3_asin(x) { + return x > 1 ? halfπ : x < -1 ? -halfπ : Math.asin(x); + } + function d3_sinh(x) { + return ((x = Math.exp(x)) - 1 / x) / 2; + } + function d3_cosh(x) { + return ((x = Math.exp(x)) + 1 / x) / 2; + } + function d3_tanh(x) { + return ((x = Math.exp(2 * x)) - 1) / (x + 1); + } + function d3_haversin(x) { + return (x = Math.sin(x / 2)) * x; + } + var ρ = Math.SQRT2, ρ2 = 2, ρ4 = 4; + d3.interpolateZoom = function(p0, p1) { + var ux0 = p0[0], uy0 = p0[1], w0 = p0[2], ux1 = p1[0], uy1 = p1[1], w1 = p1[2]; + var dx = ux1 - ux0, dy = uy1 - uy0, d2 = dx * dx + dy * dy, d1 = Math.sqrt(d2), b0 = (w1 * w1 - w0 * w0 + ρ4 * d2) / (2 * w0 * ρ2 * d1), b1 = (w1 * w1 - w0 * w0 - ρ4 * d2) / (2 * w1 * ρ2 * d1), r0 = Math.log(Math.sqrt(b0 * b0 + 1) - b0), r1 = Math.log(Math.sqrt(b1 * b1 + 1) - b1), dr = r1 - r0, S = (dr || Math.log(w1 / w0)) / ρ; + function interpolate(t) { + var s = t * S; + if (dr) { + var coshr0 = d3_cosh(r0), u = w0 / (ρ2 * d1) * (coshr0 * d3_tanh(ρ * s + r0) - d3_sinh(r0)); + return [ ux0 + u * dx, uy0 + u * dy, w0 * coshr0 / d3_cosh(ρ * s + r0) ]; + } + return [ ux0 + t * dx, uy0 + t * dy, w0 * Math.exp(ρ * s) ]; + } + interpolate.duration = S * 1e3; + return interpolate; + }; + d3.behavior.zoom = function() { + var view = { + x: 0, + y: 0, + k: 1 + }, translate0, center, size = [ 960, 500 ], scaleExtent = d3_behavior_zoomInfinity, mousedown = "mousedown.zoom", mousemove = "mousemove.zoom", mouseup = "mouseup.zoom", mousewheelTimer, touchstart = "touchstart.zoom", touchtime, event = d3_eventDispatch(zoom, "zoomstart", "zoom", "zoomend"), x0, x1, y0, y1; + function zoom(g) { + g.on(mousedown, mousedowned).on(d3_behavior_zoomWheel + ".zoom", mousewheeled).on(mousemove, mousewheelreset).on("dblclick.zoom", dblclicked).on(touchstart, touchstarted); + } + zoom.event = function(g) { + g.each(function() { + var dispatch = event.of(this, arguments), view1 = view; + if (d3_transitionInheritId) { + d3.select(this).transition().each("start.zoom", function() { + view = this.__chart__ || { + x: 0, + y: 0, + k: 1 + }; + zoomstarted(dispatch); + }).tween("zoom:zoom", function() { + var dx = size[0], dy = size[1], cx = dx / 2, cy = dy / 2, i = d3.interpolateZoom([ (cx - view.x) / view.k, (cy - view.y) / view.k, dx / view.k ], [ (cx - view1.x) / view1.k, (cy - view1.y) / view1.k, dx / view1.k ]); + return function(t) { + var l = i(t), k = dx / l[2]; + this.__chart__ = view = { + x: cx - l[0] * k, + y: cy - l[1] * k, + k: k + }; + zoomed(dispatch); + }; + }).each("end.zoom", function() { + zoomended(dispatch); + }); + } else { + this.__chart__ = view; + zoomstarted(dispatch); + zoomed(dispatch); + zoomended(dispatch); + } + }); + }; + zoom.translate = function(_) { + if (!arguments.length) return [ view.x, view.y ]; + view = { + x: +_[0], + y: +_[1], + k: view.k + }; + rescale(); + return zoom; + }; + zoom.scale = function(_) { + if (!arguments.length) return view.k; + view = { + x: view.x, + y: view.y, + k: +_ + }; + rescale(); + return zoom; + }; + zoom.scaleExtent = function(_) { + if (!arguments.length) return scaleExtent; + scaleExtent = _ == null ? d3_behavior_zoomInfinity : [ +_[0], +_[1] ]; + return zoom; + }; + zoom.center = function(_) { + if (!arguments.length) return center; + center = _ && [ +_[0], +_[1] ]; + return zoom; + }; + zoom.size = function(_) { + if (!arguments.length) return size; + size = _ && [ +_[0], +_[1] ]; + return zoom; + }; + zoom.x = function(z) { + if (!arguments.length) return x1; + x1 = z; + x0 = z.copy(); + view = { + x: 0, + y: 0, + k: 1 + }; + return zoom; + }; + zoom.y = function(z) { + if (!arguments.length) return y1; + y1 = z; + y0 = z.copy(); + view = { + x: 0, + y: 0, + k: 1 + }; + return zoom; + }; + function location(p) { + return [ (p[0] - view.x) / view.k, (p[1] - view.y) / view.k ]; + } + function point(l) { + return [ l[0] * view.k + view.x, l[1] * view.k + view.y ]; + } + function scaleTo(s) { + view.k = Math.max(scaleExtent[0], Math.min(scaleExtent[1], s)); + } + function translateTo(p, l) { + l = point(l); + view.x += p[0] - l[0]; + view.y += p[1] - l[1]; + } + function rescale() { + if (x1) x1.domain(x0.range().map(function(x) { + return (x - view.x) / view.k; + }).map(x0.invert)); + if (y1) y1.domain(y0.range().map(function(y) { + return (y - view.y) / view.k; + }).map(y0.invert)); + } + function zoomstarted(dispatch) { + dispatch({ + type: "zoomstart" + }); + } + function zoomed(dispatch) { + rescale(); + dispatch({ + type: "zoom", + scale: view.k, + translate: [ view.x, view.y ] + }); + } + function zoomended(dispatch) { + dispatch({ + type: "zoomend" + }); + } + function mousedowned() { + var that = this, target = d3.event.target, dispatch = event.of(that, arguments), dragged = 0, subject = d3.select(d3_window).on(mousemove, moved).on(mouseup, ended), location0 = location(d3.mouse(that)), dragRestore = d3_event_dragSuppress(); + d3_selection_interrupt.call(that); + zoomstarted(dispatch); + function moved() { + dragged = 1; + translateTo(d3.mouse(that), location0); + zoomed(dispatch); + } + function ended() { + subject.on(mousemove, d3_window === that ? mousewheelreset : null).on(mouseup, null); + dragRestore(dragged && d3.event.target === target); + zoomended(dispatch); + } + } + function touchstarted() { + var that = this, dispatch = event.of(that, arguments), locations0 = {}, distance0 = 0, scale0, zoomName = ".zoom-" + d3.event.changedTouches[0].identifier, touchmove = "touchmove" + zoomName, touchend = "touchend" + zoomName, targets = [], subject = d3.select(that).on(mousedown, null).on(touchstart, started), dragRestore = d3_event_dragSuppress(); + d3_selection_interrupt.call(that); + started(); + zoomstarted(dispatch); + function relocate() { + var touches = d3.touches(that); + scale0 = view.k; + touches.forEach(function(t) { + if (t.identifier in locations0) locations0[t.identifier] = location(t); + }); + return touches; + } + function started() { + var target = d3.event.target; + d3.select(target).on(touchmove, moved).on(touchend, ended); + targets.push(target); + var changed = d3.event.changedTouches; + for (var i = 0, n = changed.length; i < n; ++i) { + locations0[changed[i].identifier] = null; + } + var touches = relocate(), now = Date.now(); + if (touches.length === 1) { + if (now - touchtime < 500) { + var p = touches[0], l = locations0[p.identifier]; + scaleTo(view.k * 2); + translateTo(p, l); + d3_eventPreventDefault(); + zoomed(dispatch); + } + touchtime = now; + } else if (touches.length > 1) { + var p = touches[0], q = touches[1], dx = p[0] - q[0], dy = p[1] - q[1]; + distance0 = dx * dx + dy * dy; + } + } + function moved() { + var touches = d3.touches(that), p0, l0, p1, l1; + for (var i = 0, n = touches.length; i < n; ++i, l1 = null) { + p1 = touches[i]; + if (l1 = locations0[p1.identifier]) { + if (l0) break; + p0 = p1, l0 = l1; + } + } + if (l1) { + var distance1 = (distance1 = p1[0] - p0[0]) * distance1 + (distance1 = p1[1] - p0[1]) * distance1, scale1 = distance0 && Math.sqrt(distance1 / distance0); + p0 = [ (p0[0] + p1[0]) / 2, (p0[1] + p1[1]) / 2 ]; + l0 = [ (l0[0] + l1[0]) / 2, (l0[1] + l1[1]) / 2 ]; + scaleTo(scale1 * scale0); + } + touchtime = null; + translateTo(p0, l0); + zoomed(dispatch); + } + function ended() { + if (d3.event.touches.length) { + var changed = d3.event.changedTouches; + for (var i = 0, n = changed.length; i < n; ++i) { + delete locations0[changed[i].identifier]; + } + for (var identifier in locations0) { + return void relocate(); + } + } + d3.selectAll(targets).on(zoomName, null); + subject.on(mousedown, mousedowned).on(touchstart, touchstarted); + dragRestore(); + zoomended(dispatch); + } + } + function mousewheeled() { + var dispatch = event.of(this, arguments); + if (mousewheelTimer) clearTimeout(mousewheelTimer); else d3_selection_interrupt.call(this), + zoomstarted(dispatch); + mousewheelTimer = setTimeout(function() { + mousewheelTimer = null; + zoomended(dispatch); + }, 50); + d3_eventPreventDefault(); + var point = center || d3.mouse(this); + if (!translate0) translate0 = location(point); + scaleTo(Math.pow(2, d3_behavior_zoomDelta() * .002) * view.k); + translateTo(point, translate0); + zoomed(dispatch); + } + function mousewheelreset() { + translate0 = null; + } + function dblclicked() { + var dispatch = event.of(this, arguments), p = d3.mouse(this), l = location(p), k = Math.log(view.k) / Math.LN2; + zoomstarted(dispatch); + scaleTo(Math.pow(2, d3.event.shiftKey ? Math.ceil(k) - 1 : Math.floor(k) + 1)); + translateTo(p, l); + zoomed(dispatch); + zoomended(dispatch); + } + return d3.rebind(zoom, event, "on"); + }; + var d3_behavior_zoomInfinity = [ 0, Infinity ]; + var d3_behavior_zoomDelta, d3_behavior_zoomWheel = "onwheel" in d3_document ? (d3_behavior_zoomDelta = function() { + return -d3.event.deltaY * (d3.event.deltaMode ? 120 : 1); + }, "wheel") : "onmousewheel" in d3_document ? (d3_behavior_zoomDelta = function() { + return d3.event.wheelDelta; + }, "mousewheel") : (d3_behavior_zoomDelta = function() { + return -d3.event.detail; + }, "MozMousePixelScroll"); + function d3_Color() {} + d3_Color.prototype.toString = function() { + return this.rgb() + ""; + }; + d3.hsl = function(h, s, l) { + return arguments.length === 1 ? h instanceof d3_Hsl ? d3_hsl(h.h, h.s, h.l) : d3_rgb_parse("" + h, d3_rgb_hsl, d3_hsl) : d3_hsl(+h, +s, +l); + }; + function d3_hsl(h, s, l) { + return new d3_Hsl(h, s, l); + } + function d3_Hsl(h, s, l) { + this.h = h; + this.s = s; + this.l = l; + } + var d3_hslPrototype = d3_Hsl.prototype = new d3_Color(); + d3_hslPrototype.brighter = function(k) { + k = Math.pow(.7, arguments.length ? k : 1); + return d3_hsl(this.h, this.s, this.l / k); + }; + d3_hslPrototype.darker = function(k) { + k = Math.pow(.7, arguments.length ? k : 1); + return d3_hsl(this.h, this.s, k * this.l); + }; + d3_hslPrototype.rgb = function() { + return d3_hsl_rgb(this.h, this.s, this.l); + }; + function d3_hsl_rgb(h, s, l) { + var m1, m2; + h = isNaN(h) ? 0 : (h %= 360) < 0 ? h + 360 : h; + s = isNaN(s) ? 0 : s < 0 ? 0 : s > 1 ? 1 : s; + l = l < 0 ? 0 : l > 1 ? 1 : l; + m2 = l <= .5 ? l * (1 + s) : l + s - l * s; + m1 = 2 * l - m2; + function v(h) { + if (h > 360) h -= 360; else if (h < 0) h += 360; + if (h < 60) return m1 + (m2 - m1) * h / 60; + if (h < 180) return m2; + if (h < 240) return m1 + (m2 - m1) * (240 - h) / 60; + return m1; + } + function vv(h) { + return Math.round(v(h) * 255); + } + return d3_rgb(vv(h + 120), vv(h), vv(h - 120)); + } + d3.hcl = function(h, c, l) { + return arguments.length === 1 ? h instanceof d3_Hcl ? d3_hcl(h.h, h.c, h.l) : h instanceof d3_Lab ? d3_lab_hcl(h.l, h.a, h.b) : d3_lab_hcl((h = d3_rgb_lab((h = d3.rgb(h)).r, h.g, h.b)).l, h.a, h.b) : d3_hcl(+h, +c, +l); + }; + function d3_hcl(h, c, l) { + return new d3_Hcl(h, c, l); + } + function d3_Hcl(h, c, l) { + this.h = h; + this.c = c; + this.l = l; + } + var d3_hclPrototype = d3_Hcl.prototype = new d3_Color(); + d3_hclPrototype.brighter = function(k) { + return d3_hcl(this.h, this.c, Math.min(100, this.l + d3_lab_K * (arguments.length ? k : 1))); + }; + d3_hclPrototype.darker = function(k) { + return d3_hcl(this.h, this.c, Math.max(0, this.l - d3_lab_K * (arguments.length ? k : 1))); + }; + d3_hclPrototype.rgb = function() { + return d3_hcl_lab(this.h, this.c, this.l).rgb(); + }; + function d3_hcl_lab(h, c, l) { + if (isNaN(h)) h = 0; + if (isNaN(c)) c = 0; + return d3_lab(l, Math.cos(h *= d3_radians) * c, Math.sin(h) * c); + } + d3.lab = function(l, a, b) { + return arguments.length === 1 ? l instanceof d3_Lab ? d3_lab(l.l, l.a, l.b) : l instanceof d3_Hcl ? d3_hcl_lab(l.l, l.c, l.h) : d3_rgb_lab((l = d3.rgb(l)).r, l.g, l.b) : d3_lab(+l, +a, +b); + }; + function d3_lab(l, a, b) { + return new d3_Lab(l, a, b); + } + function d3_Lab(l, a, b) { + this.l = l; + this.a = a; + this.b = b; + } + var d3_lab_K = 18; + var d3_lab_X = .95047, d3_lab_Y = 1, d3_lab_Z = 1.08883; + var d3_labPrototype = d3_Lab.prototype = new d3_Color(); + d3_labPrototype.brighter = function(k) { + return d3_lab(Math.min(100, this.l + d3_lab_K * (arguments.length ? k : 1)), this.a, this.b); + }; + d3_labPrototype.darker = function(k) { + return d3_lab(Math.max(0, this.l - d3_lab_K * (arguments.length ? k : 1)), this.a, this.b); + }; + d3_labPrototype.rgb = function() { + return d3_lab_rgb(this.l, this.a, this.b); + }; + function d3_lab_rgb(l, a, b) { + var y = (l + 16) / 116, x = y + a / 500, z = y - b / 200; + x = d3_lab_xyz(x) * d3_lab_X; + y = d3_lab_xyz(y) * d3_lab_Y; + z = d3_lab_xyz(z) * d3_lab_Z; + return d3_rgb(d3_xyz_rgb(3.2404542 * x - 1.5371385 * y - .4985314 * z), d3_xyz_rgb(-.969266 * x + 1.8760108 * y + .041556 * z), d3_xyz_rgb(.0556434 * x - .2040259 * y + 1.0572252 * z)); + } + function d3_lab_hcl(l, a, b) { + return l > 0 ? d3_hcl(Math.atan2(b, a) * d3_degrees, Math.sqrt(a * a + b * b), l) : d3_hcl(NaN, NaN, l); + } + function d3_lab_xyz(x) { + return x > .206893034 ? x * x * x : (x - 4 / 29) / 7.787037; + } + function d3_xyz_lab(x) { + return x > .008856 ? Math.pow(x, 1 / 3) : 7.787037 * x + 4 / 29; + } + function d3_xyz_rgb(r) { + return Math.round(255 * (r <= .00304 ? 12.92 * r : 1.055 * Math.pow(r, 1 / 2.4) - .055)); + } + d3.rgb = function(r, g, b) { + return arguments.length === 1 ? r instanceof d3_Rgb ? d3_rgb(r.r, r.g, r.b) : d3_rgb_parse("" + r, d3_rgb, d3_hsl_rgb) : d3_rgb(~~r, ~~g, ~~b); + }; + function d3_rgbNumber(value) { + return d3_rgb(value >> 16, value >> 8 & 255, value & 255); + } + function d3_rgbString(value) { + return d3_rgbNumber(value) + ""; + } + function d3_rgb(r, g, b) { + return new d3_Rgb(r, g, b); + } + function d3_Rgb(r, g, b) { + this.r = r; + this.g = g; + this.b = b; + } + var d3_rgbPrototype = d3_Rgb.prototype = new d3_Color(); + d3_rgbPrototype.brighter = function(k) { + k = Math.pow(.7, arguments.length ? k : 1); + var r = this.r, g = this.g, b = this.b, i = 30; + if (!r && !g && !b) return d3_rgb(i, i, i); + if (r && r < i) r = i; + if (g && g < i) g = i; + if (b && b < i) b = i; + return d3_rgb(Math.min(255, ~~(r / k)), Math.min(255, ~~(g / k)), Math.min(255, ~~(b / k))); + }; + d3_rgbPrototype.darker = function(k) { + k = Math.pow(.7, arguments.length ? k : 1); + return d3_rgb(~~(k * this.r), ~~(k * this.g), ~~(k * this.b)); + }; + d3_rgbPrototype.hsl = function() { + return d3_rgb_hsl(this.r, this.g, this.b); + }; + d3_rgbPrototype.toString = function() { + return "#" + d3_rgb_hex(this.r) + d3_rgb_hex(this.g) + d3_rgb_hex(this.b); + }; + function d3_rgb_hex(v) { + return v < 16 ? "0" + Math.max(0, v).toString(16) : Math.min(255, v).toString(16); + } + function d3_rgb_parse(format, rgb, hsl) { + var r = 0, g = 0, b = 0, m1, m2, color; + m1 = /([a-z]+)\((.*)\)/i.exec(format); + if (m1) { + m2 = m1[2].split(","); + switch (m1[1]) { + case "hsl": + { + return hsl(parseFloat(m2[0]), parseFloat(m2[1]) / 100, parseFloat(m2[2]) / 100); + } + + case "rgb": + { + return rgb(d3_rgb_parseNumber(m2[0]), d3_rgb_parseNumber(m2[1]), d3_rgb_parseNumber(m2[2])); + } + } + } + if (color = d3_rgb_names.get(format)) return rgb(color.r, color.g, color.b); + if (format != null && format.charAt(0) === "#" && !isNaN(color = parseInt(format.substring(1), 16))) { + if (format.length === 4) { + r = (color & 3840) >> 4; + r = r >> 4 | r; + g = color & 240; + g = g >> 4 | g; + b = color & 15; + b = b << 4 | b; + } else if (format.length === 7) { + r = (color & 16711680) >> 16; + g = (color & 65280) >> 8; + b = color & 255; + } + } + return rgb(r, g, b); + } + function d3_rgb_hsl(r, g, b) { + var min = Math.min(r /= 255, g /= 255, b /= 255), max = Math.max(r, g, b), d = max - min, h, s, l = (max + min) / 2; + if (d) { + s = l < .5 ? d / (max + min) : d / (2 - max - min); + if (r == max) h = (g - b) / d + (g < b ? 6 : 0); else if (g == max) h = (b - r) / d + 2; else h = (r - g) / d + 4; + h *= 60; + } else { + h = NaN; + s = l > 0 && l < 1 ? 0 : h; + } + return d3_hsl(h, s, l); + } + function d3_rgb_lab(r, g, b) { + r = d3_rgb_xyz(r); + g = d3_rgb_xyz(g); + b = d3_rgb_xyz(b); + var x = d3_xyz_lab((.4124564 * r + .3575761 * g + .1804375 * b) / d3_lab_X), y = d3_xyz_lab((.2126729 * r + .7151522 * g + .072175 * b) / d3_lab_Y), z = d3_xyz_lab((.0193339 * r + .119192 * g + .9503041 * b) / d3_lab_Z); + return d3_lab(116 * y - 16, 500 * (x - y), 200 * (y - z)); + } + function d3_rgb_xyz(r) { + return (r /= 255) <= .04045 ? r / 12.92 : Math.pow((r + .055) / 1.055, 2.4); + } + function d3_rgb_parseNumber(c) { + var f = parseFloat(c); + return c.charAt(c.length - 1) === "%" ? Math.round(f * 2.55) : f; + } + var d3_rgb_names = d3.map({ + aliceblue: 15792383, + antiquewhite: 16444375, + aqua: 65535, + aquamarine: 8388564, + azure: 15794175, + beige: 16119260, + bisque: 16770244, + black: 0, + blanchedalmond: 16772045, + blue: 255, + blueviolet: 9055202, + brown: 10824234, + burlywood: 14596231, + cadetblue: 6266528, + chartreuse: 8388352, + chocolate: 13789470, + coral: 16744272, + cornflowerblue: 6591981, + cornsilk: 16775388, + crimson: 14423100, + cyan: 65535, + darkblue: 139, + darkcyan: 35723, + darkgoldenrod: 12092939, + darkgray: 11119017, + darkgreen: 25600, + darkgrey: 11119017, + darkkhaki: 12433259, + darkmagenta: 9109643, + darkolivegreen: 5597999, + darkorange: 16747520, + darkorchid: 10040012, + darkred: 9109504, + darksalmon: 15308410, + darkseagreen: 9419919, + darkslateblue: 4734347, + darkslategray: 3100495, + darkslategrey: 3100495, + darkturquoise: 52945, + darkviolet: 9699539, + deeppink: 16716947, + deepskyblue: 49151, + dimgray: 6908265, + dimgrey: 6908265, + dodgerblue: 2003199, + firebrick: 11674146, + floralwhite: 16775920, + forestgreen: 2263842, + fuchsia: 16711935, + gainsboro: 14474460, + ghostwhite: 16316671, + gold: 16766720, + goldenrod: 14329120, + gray: 8421504, + green: 32768, + greenyellow: 11403055, + grey: 8421504, + honeydew: 15794160, + hotpink: 16738740, + indianred: 13458524, + indigo: 4915330, + ivory: 16777200, + khaki: 15787660, + lavender: 15132410, + lavenderblush: 16773365, + lawngreen: 8190976, + lemonchiffon: 16775885, + lightblue: 11393254, + lightcoral: 15761536, + lightcyan: 14745599, + lightgoldenrodyellow: 16448210, + lightgray: 13882323, + lightgreen: 9498256, + lightgrey: 13882323, + lightpink: 16758465, + lightsalmon: 16752762, + lightseagreen: 2142890, + lightskyblue: 8900346, + lightslategray: 7833753, + lightslategrey: 7833753, + lightsteelblue: 11584734, + lightyellow: 16777184, + lime: 65280, + limegreen: 3329330, + linen: 16445670, + magenta: 16711935, + maroon: 8388608, + mediumaquamarine: 6737322, + mediumblue: 205, + mediumorchid: 12211667, + mediumpurple: 9662683, + mediumseagreen: 3978097, + mediumslateblue: 8087790, + mediumspringgreen: 64154, + mediumturquoise: 4772300, + mediumvioletred: 13047173, + midnightblue: 1644912, + mintcream: 16121850, + mistyrose: 16770273, + moccasin: 16770229, + navajowhite: 16768685, + navy: 128, + oldlace: 16643558, + olive: 8421376, + olivedrab: 7048739, + orange: 16753920, + orangered: 16729344, + orchid: 14315734, + palegoldenrod: 15657130, + palegreen: 10025880, + paleturquoise: 11529966, + palevioletred: 14381203, + papayawhip: 16773077, + peachpuff: 16767673, + peru: 13468991, + pink: 16761035, + plum: 14524637, + powderblue: 11591910, + purple: 8388736, + red: 16711680, + rosybrown: 12357519, + royalblue: 4286945, + saddlebrown: 9127187, + salmon: 16416882, + sandybrown: 16032864, + seagreen: 3050327, + seashell: 16774638, + sienna: 10506797, + silver: 12632256, + skyblue: 8900331, + slateblue: 6970061, + slategray: 7372944, + slategrey: 7372944, + snow: 16775930, + springgreen: 65407, + steelblue: 4620980, + tan: 13808780, + teal: 32896, + thistle: 14204888, + tomato: 16737095, + turquoise: 4251856, + violet: 15631086, + wheat: 16113331, + white: 16777215, + whitesmoke: 16119285, + yellow: 16776960, + yellowgreen: 10145074 + }); + d3_rgb_names.forEach(function(key, value) { + d3_rgb_names.set(key, d3_rgbNumber(value)); + }); + function d3_functor(v) { + return typeof v === "function" ? v : function() { + return v; + }; + } + d3.functor = d3_functor; + function d3_identity(d) { + return d; + } + d3.xhr = d3_xhrType(d3_identity); + function d3_xhrType(response) { + return function(url, mimeType, callback) { + if (arguments.length === 2 && typeof mimeType === "function") callback = mimeType, + mimeType = null; + return d3_xhr(url, mimeType, response, callback); + }; + } + function d3_xhr(url, mimeType, response, callback) { + var xhr = {}, dispatch = d3.dispatch("beforesend", "progress", "load", "error"), headers = {}, request = new XMLHttpRequest(), responseType = null; + if (d3_window.XDomainRequest && !("withCredentials" in request) && /^(http(s)?:)?\/\//.test(url)) request = new XDomainRequest(); + "onload" in request ? request.onload = request.onerror = respond : request.onreadystatechange = function() { + request.readyState > 3 && respond(); + }; + function respond() { + var status = request.status, result; + if (!status && request.responseText || status >= 200 && status < 300 || status === 304) { + try { + result = response.call(xhr, request); + } catch (e) { + dispatch.error.call(xhr, e); + return; + } + dispatch.load.call(xhr, result); + } else { + dispatch.error.call(xhr, request); + } + } + request.onprogress = function(event) { + var o = d3.event; + d3.event = event; + try { + dispatch.progress.call(xhr, request); + } finally { + d3.event = o; + } + }; + xhr.header = function(name, value) { + name = (name + "").toLowerCase(); + if (arguments.length < 2) return headers[name]; + if (value == null) delete headers[name]; else headers[name] = value + ""; + return xhr; + }; + xhr.mimeType = function(value) { + if (!arguments.length) return mimeType; + mimeType = value == null ? null : value + ""; + return xhr; + }; + xhr.responseType = function(value) { + if (!arguments.length) return responseType; + responseType = value; + return xhr; + }; + xhr.response = function(value) { + response = value; + return xhr; + }; + [ "get", "post" ].forEach(function(method) { + xhr[method] = function() { + return xhr.send.apply(xhr, [ method ].concat(d3_array(arguments))); + }; + }); + xhr.send = function(method, data, callback) { + if (arguments.length === 2 && typeof data === "function") callback = data, data = null; + request.open(method, url, true); + if (mimeType != null && !("accept" in headers)) headers["accept"] = mimeType + ",*/*"; + if (request.setRequestHeader) for (var name in headers) request.setRequestHeader(name, headers[name]); + if (mimeType != null && request.overrideMimeType) request.overrideMimeType(mimeType); + if (responseType != null) request.responseType = responseType; + if (callback != null) xhr.on("error", callback).on("load", function(request) { + callback(null, request); + }); + dispatch.beforesend.call(xhr, request); + request.send(data == null ? null : data); + return xhr; + }; + xhr.abort = function() { + request.abort(); + return xhr; + }; + d3.rebind(xhr, dispatch, "on"); + return callback == null ? xhr : xhr.get(d3_xhr_fixCallback(callback)); + } + function d3_xhr_fixCallback(callback) { + return callback.length === 1 ? function(error, request) { + callback(error == null ? request : null); + } : callback; + } + d3.dsv = function(delimiter, mimeType) { + var reFormat = new RegExp('["' + delimiter + "\n]"), delimiterCode = delimiter.charCodeAt(0); + function dsv(url, row, callback) { + if (arguments.length < 3) callback = row, row = null; + var xhr = d3_xhr(url, mimeType, row == null ? response : typedResponse(row), callback); + xhr.row = function(_) { + return arguments.length ? xhr.response((row = _) == null ? response : typedResponse(_)) : row; + }; + return xhr; + } + function response(request) { + return dsv.parse(request.responseText); + } + function typedResponse(f) { + return function(request) { + return dsv.parse(request.responseText, f); + }; + } + dsv.parse = function(text, f) { + var o; + return dsv.parseRows(text, function(row, i) { + if (o) return o(row, i - 1); + var a = new Function("d", "return {" + row.map(function(name, i) { + return JSON.stringify(name) + ": d[" + i + "]"; + }).join(",") + "}"); + o = f ? function(row, i) { + return f(a(row), i); + } : a; + }); + }; + dsv.parseRows = function(text, f) { + var EOL = {}, EOF = {}, rows = [], N = text.length, I = 0, n = 0, t, eol; + function token() { + if (I >= N) return EOF; + if (eol) return eol = false, EOL; + var j = I; + if (text.charCodeAt(j) === 34) { + var i = j; + while (i++ < N) { + if (text.charCodeAt(i) === 34) { + if (text.charCodeAt(i + 1) !== 34) break; + ++i; + } + } + I = i + 2; + var c = text.charCodeAt(i + 1); + if (c === 13) { + eol = true; + if (text.charCodeAt(i + 2) === 10) ++I; + } else if (c === 10) { + eol = true; + } + return text.substring(j + 1, i).replace(/""/g, '"'); + } + while (I < N) { + var c = text.charCodeAt(I++), k = 1; + if (c === 10) eol = true; else if (c === 13) { + eol = true; + if (text.charCodeAt(I) === 10) ++I, ++k; + } else if (c !== delimiterCode) continue; + return text.substring(j, I - k); + } + return text.substring(j); + } + while ((t = token()) !== EOF) { + var a = []; + while (t !== EOL && t !== EOF) { + a.push(t); + t = token(); + } + if (f && !(a = f(a, n++))) continue; + rows.push(a); + } + return rows; + }; + dsv.format = function(rows) { + if (Array.isArray(rows[0])) return dsv.formatRows(rows); + var fieldSet = new d3_Set(), fields = []; + rows.forEach(function(row) { + for (var field in row) { + if (!fieldSet.has(field)) { + fields.push(fieldSet.add(field)); + } + } + }); + return [ fields.map(formatValue).join(delimiter) ].concat(rows.map(function(row) { + return fields.map(function(field) { + return formatValue(row[field]); + }).join(delimiter); + })).join("\n"); + }; + dsv.formatRows = function(rows) { + return rows.map(formatRow).join("\n"); + }; + function formatRow(row) { + return row.map(formatValue).join(delimiter); + } + function formatValue(text) { + return reFormat.test(text) ? '"' + text.replace(/\"/g, '""') + '"' : text; + } + return dsv; + }; + d3.csv = d3.dsv(",", "text/csv"); + d3.tsv = d3.dsv(" ", "text/tab-separated-values"); + d3.touch = function(container, touches, identifier) { + if (arguments.length < 3) identifier = touches, touches = d3_eventSource().changedTouches; + if (touches) for (var i = 0, n = touches.length, touch; i < n; ++i) { + if ((touch = touches[i]).identifier === identifier) { + return d3_mousePoint(container, touch); + } + } + }; + var d3_timer_queueHead, d3_timer_queueTail, d3_timer_interval, d3_timer_timeout, d3_timer_active, d3_timer_frame = d3_window[d3_vendorSymbol(d3_window, "requestAnimationFrame")] || function(callback) { + setTimeout(callback, 17); + }; + d3.timer = function(callback, delay, then) { + var n = arguments.length; + if (n < 2) delay = 0; + if (n < 3) then = Date.now(); + var time = then + delay, timer = { + c: callback, + t: time, + f: false, + n: null + }; + if (d3_timer_queueTail) d3_timer_queueTail.n = timer; else d3_timer_queueHead = timer; + d3_timer_queueTail = timer; + if (!d3_timer_interval) { + d3_timer_timeout = clearTimeout(d3_timer_timeout); + d3_timer_interval = 1; + d3_timer_frame(d3_timer_step); + } + }; + function d3_timer_step() { + var now = d3_timer_mark(), delay = d3_timer_sweep() - now; + if (delay > 24) { + if (isFinite(delay)) { + clearTimeout(d3_timer_timeout); + d3_timer_timeout = setTimeout(d3_timer_step, delay); + } + d3_timer_interval = 0; + } else { + d3_timer_interval = 1; + d3_timer_frame(d3_timer_step); + } + } + d3.timer.flush = function() { + d3_timer_mark(); + d3_timer_sweep(); + }; + function d3_timer_mark() { + var now = Date.now(); + d3_timer_active = d3_timer_queueHead; + while (d3_timer_active) { + if (now >= d3_timer_active.t) d3_timer_active.f = d3_timer_active.c(now - d3_timer_active.t); + d3_timer_active = d3_timer_active.n; + } + return now; + } + function d3_timer_sweep() { + var t0, t1 = d3_timer_queueHead, time = Infinity; + while (t1) { + if (t1.f) { + t1 = t0 ? t0.n = t1.n : d3_timer_queueHead = t1.n; + } else { + if (t1.t < time) time = t1.t; + t1 = (t0 = t1).n; + } + } + d3_timer_queueTail = t0; + return time; + } + function d3_format_precision(x, p) { + return p - (x ? Math.ceil(Math.log(x) / Math.LN10) : 1); + } + d3.round = function(x, n) { + return n ? Math.round(x * (n = Math.pow(10, n))) / n : Math.round(x); + }; + var d3_formatPrefixes = [ "y", "z", "a", "f", "p", "n", "µ", "m", "", "k", "M", "G", "T", "P", "E", "Z", "Y" ].map(d3_formatPrefix); + d3.formatPrefix = function(value, precision) { + var i = 0; + if (value) { + if (value < 0) value *= -1; + if (precision) value = d3.round(value, d3_format_precision(value, precision)); + i = 1 + Math.floor(1e-12 + Math.log(value) / Math.LN10); + i = Math.max(-24, Math.min(24, Math.floor((i - 1) / 3) * 3)); + } + return d3_formatPrefixes[8 + i / 3]; + }; + function d3_formatPrefix(d, i) { + var k = Math.pow(10, abs(8 - i) * 3); + return { + scale: i > 8 ? function(d) { + return d / k; + } : function(d) { + return d * k; + }, + symbol: d + }; + } + function d3_locale_numberFormat(locale) { + var locale_decimal = locale.decimal, locale_thousands = locale.thousands, locale_grouping = locale.grouping, locale_currency = locale.currency, formatGroup = locale_grouping ? function(value) { + var i = value.length, t = [], j = 0, g = locale_grouping[0]; + while (i > 0 && g > 0) { + t.push(value.substring(i -= g, i + g)); + g = locale_grouping[j = (j + 1) % locale_grouping.length]; + } + return t.reverse().join(locale_thousands); + } : d3_identity; + return function(specifier) { + var match = d3_format_re.exec(specifier), fill = match[1] || " ", align = match[2] || ">", sign = match[3] || "", symbol = match[4] || "", zfill = match[5], width = +match[6], comma = match[7], precision = match[8], type = match[9], scale = 1, prefix = "", suffix = "", integer = false; + if (precision) precision = +precision.substring(1); + if (zfill || fill === "0" && align === "=") { + zfill = fill = "0"; + align = "="; + if (comma) width -= Math.floor((width - 1) / 4); + } + switch (type) { + case "n": + comma = true; + type = "g"; + break; + + case "%": + scale = 100; + suffix = "%"; + type = "f"; + break; + + case "p": + scale = 100; + suffix = "%"; + type = "r"; + break; + + case "b": + case "o": + case "x": + case "X": + if (symbol === "#") prefix = "0" + type.toLowerCase(); + + case "c": + case "d": + integer = true; + precision = 0; + break; + + case "s": + scale = -1; + type = "r"; + break; + } + if (symbol === "$") prefix = locale_currency[0], suffix = locale_currency[1]; + if (type == "r" && !precision) type = "g"; + if (precision != null) { + if (type == "g") precision = Math.max(1, Math.min(21, precision)); else if (type == "e" || type == "f") precision = Math.max(0, Math.min(20, precision)); + } + type = d3_format_types.get(type) || d3_format_typeDefault; + var zcomma = zfill && comma; + return function(value) { + var fullSuffix = suffix; + if (integer && value % 1) return ""; + var negative = value < 0 || value === 0 && 1 / value < 0 ? (value = -value, "-") : sign; + if (scale < 0) { + var unit = d3.formatPrefix(value, precision); + value = unit.scale(value); + fullSuffix = unit.symbol + suffix; + } else { + value *= scale; + } + value = type(value, precision); + var i = value.lastIndexOf("."), before = i < 0 ? value : value.substring(0, i), after = i < 0 ? "" : locale_decimal + value.substring(i + 1); + if (!zfill && comma) before = formatGroup(before); + var length = prefix.length + before.length + after.length + (zcomma ? 0 : negative.length), padding = length < width ? new Array(length = width - length + 1).join(fill) : ""; + if (zcomma) before = formatGroup(padding + before); + negative += prefix; + value = before + after; + return (align === "<" ? negative + value + padding : align === ">" ? padding + negative + value : align === "^" ? padding.substring(0, length >>= 1) + negative + value + padding.substring(length) : negative + (zcomma ? value : padding + value)) + fullSuffix; + }; + }; + } + var d3_format_re = /(?:([^{])?([<>=^]))?([+\- ])?([$#])?(0)?(\d+)?(,)?(\.-?\d+)?([a-z%])?/i; + var d3_format_types = d3.map({ + b: function(x) { + return x.toString(2); + }, + c: function(x) { + return String.fromCharCode(x); + }, + o: function(x) { + return x.toString(8); + }, + x: function(x) { + return x.toString(16); + }, + X: function(x) { + return x.toString(16).toUpperCase(); + }, + g: function(x, p) { + return x.toPrecision(p); + }, + e: function(x, p) { + return x.toExponential(p); + }, + f: function(x, p) { + return x.toFixed(p); + }, + r: function(x, p) { + return (x = d3.round(x, d3_format_precision(x, p))).toFixed(Math.max(0, Math.min(20, d3_format_precision(x * (1 + 1e-15), p)))); + } + }); + function d3_format_typeDefault(x) { + return x + ""; + } + var d3_time = d3.time = {}, d3_date = Date; + function d3_date_utc() { + this._ = new Date(arguments.length > 1 ? Date.UTC.apply(this, arguments) : arguments[0]); + } + d3_date_utc.prototype = { + getDate: function() { + return this._.getUTCDate(); + }, + getDay: function() { + return this._.getUTCDay(); + }, + getFullYear: function() { + return this._.getUTCFullYear(); + }, + getHours: function() { + return this._.getUTCHours(); + }, + getMilliseconds: function() { + return this._.getUTCMilliseconds(); + }, + getMinutes: function() { + return this._.getUTCMinutes(); + }, + getMonth: function() { + return this._.getUTCMonth(); + }, + getSeconds: function() { + return this._.getUTCSeconds(); + }, + getTime: function() { + return this._.getTime(); + }, + getTimezoneOffset: function() { + return 0; + }, + valueOf: function() { + return this._.valueOf(); + }, + setDate: function() { + d3_time_prototype.setUTCDate.apply(this._, arguments); + }, + setDay: function() { + d3_time_prototype.setUTCDay.apply(this._, arguments); + }, + setFullYear: function() { + d3_time_prototype.setUTCFullYear.apply(this._, arguments); + }, + setHours: function() { + d3_time_prototype.setUTCHours.apply(this._, arguments); + }, + setMilliseconds: function() { + d3_time_prototype.setUTCMilliseconds.apply(this._, arguments); + }, + setMinutes: function() { + d3_time_prototype.setUTCMinutes.apply(this._, arguments); + }, + setMonth: function() { + d3_time_prototype.setUTCMonth.apply(this._, arguments); + }, + setSeconds: function() { + d3_time_prototype.setUTCSeconds.apply(this._, arguments); + }, + setTime: function() { + d3_time_prototype.setTime.apply(this._, arguments); + } + }; + var d3_time_prototype = Date.prototype; + function d3_time_interval(local, step, number) { + function round(date) { + var d0 = local(date), d1 = offset(d0, 1); + return date - d0 < d1 - date ? d0 : d1; + } + function ceil(date) { + step(date = local(new d3_date(date - 1)), 1); + return date; + } + function offset(date, k) { + step(date = new d3_date(+date), k); + return date; + } + function range(t0, t1, dt) { + var time = ceil(t0), times = []; + if (dt > 1) { + while (time < t1) { + if (!(number(time) % dt)) times.push(new Date(+time)); + step(time, 1); + } + } else { + while (time < t1) times.push(new Date(+time)), step(time, 1); + } + return times; + } + function range_utc(t0, t1, dt) { + try { + d3_date = d3_date_utc; + var utc = new d3_date_utc(); + utc._ = t0; + return range(utc, t1, dt); + } finally { + d3_date = Date; + } + } + local.floor = local; + local.round = round; + local.ceil = ceil; + local.offset = offset; + local.range = range; + var utc = local.utc = d3_time_interval_utc(local); + utc.floor = utc; + utc.round = d3_time_interval_utc(round); + utc.ceil = d3_time_interval_utc(ceil); + utc.offset = d3_time_interval_utc(offset); + utc.range = range_utc; + return local; + } + function d3_time_interval_utc(method) { + return function(date, k) { + try { + d3_date = d3_date_utc; + var utc = new d3_date_utc(); + utc._ = date; + return method(utc, k)._; + } finally { + d3_date = Date; + } + }; + } + d3_time.year = d3_time_interval(function(date) { + date = d3_time.day(date); + date.setMonth(0, 1); + return date; + }, function(date, offset) { + date.setFullYear(date.getFullYear() + offset); + }, function(date) { + return date.getFullYear(); + }); + d3_time.years = d3_time.year.range; + d3_time.years.utc = d3_time.year.utc.range; + d3_time.day = d3_time_interval(function(date) { + var day = new d3_date(2e3, 0); + day.setFullYear(date.getFullYear(), date.getMonth(), date.getDate()); + return day; + }, function(date, offset) { + date.setDate(date.getDate() + offset); + }, function(date) { + return date.getDate() - 1; + }); + d3_time.days = d3_time.day.range; + d3_time.days.utc = d3_time.day.utc.range; + d3_time.dayOfYear = function(date) { + var year = d3_time.year(date); + return Math.floor((date - year - (date.getTimezoneOffset() - year.getTimezoneOffset()) * 6e4) / 864e5); + }; + [ "sunday", "monday", "tuesday", "wednesday", "thursday", "friday", "saturday" ].forEach(function(day, i) { + i = 7 - i; + var interval = d3_time[day] = d3_time_interval(function(date) { + (date = d3_time.day(date)).setDate(date.getDate() - (date.getDay() + i) % 7); + return date; + }, function(date, offset) { + date.setDate(date.getDate() + Math.floor(offset) * 7); + }, function(date) { + var day = d3_time.year(date).getDay(); + return Math.floor((d3_time.dayOfYear(date) + (day + i) % 7) / 7) - (day !== i); + }); + d3_time[day + "s"] = interval.range; + d3_time[day + "s"].utc = interval.utc.range; + d3_time[day + "OfYear"] = function(date) { + var day = d3_time.year(date).getDay(); + return Math.floor((d3_time.dayOfYear(date) + (day + i) % 7) / 7); + }; + }); + d3_time.week = d3_time.sunday; + d3_time.weeks = d3_time.sunday.range; + d3_time.weeks.utc = d3_time.sunday.utc.range; + d3_time.weekOfYear = d3_time.sundayOfYear; + function d3_locale_timeFormat(locale) { + var locale_dateTime = locale.dateTime, locale_date = locale.date, locale_time = locale.time, locale_periods = locale.periods, locale_days = locale.days, locale_shortDays = locale.shortDays, locale_months = locale.months, locale_shortMonths = locale.shortMonths; + function d3_time_format(template) { + var n = template.length; + function format(date) { + var string = [], i = -1, j = 0, c, p, f; + while (++i < n) { + if (template.charCodeAt(i) === 37) { + string.push(template.substring(j, i)); + if ((p = d3_time_formatPads[c = template.charAt(++i)]) != null) c = template.charAt(++i); + if (f = d3_time_formats[c]) c = f(date, p == null ? c === "e" ? " " : "0" : p); + string.push(c); + j = i + 1; + } + } + string.push(template.substring(j, i)); + return string.join(""); + } + format.parse = function(string) { + var d = { + y: 1900, + m: 0, + d: 1, + H: 0, + M: 0, + S: 0, + L: 0, + Z: null + }, i = d3_time_parse(d, template, string, 0); + if (i != string.length) return null; + if ("p" in d) d.H = d.H % 12 + d.p * 12; + var localZ = d.Z != null && d3_date !== d3_date_utc, date = new (localZ ? d3_date_utc : d3_date)(); + if ("j" in d) date.setFullYear(d.y, 0, d.j); else if ("w" in d && ("W" in d || "U" in d)) { + date.setFullYear(d.y, 0, 1); + date.setFullYear(d.y, 0, "W" in d ? (d.w + 6) % 7 + d.W * 7 - (date.getDay() + 5) % 7 : d.w + d.U * 7 - (date.getDay() + 6) % 7); + } else date.setFullYear(d.y, d.m, d.d); + date.setHours(d.H + Math.floor(d.Z / 100), d.M + d.Z % 100, d.S, d.L); + return localZ ? date._ : date; + }; + format.toString = function() { + return template; + }; + return format; + } + function d3_time_parse(date, template, string, j) { + var c, p, t, i = 0, n = template.length, m = string.length; + while (i < n) { + if (j >= m) return -1; + c = template.charCodeAt(i++); + if (c === 37) { + t = template.charAt(i++); + p = d3_time_parsers[t in d3_time_formatPads ? template.charAt(i++) : t]; + if (!p || (j = p(date, string, j)) < 0) return -1; + } else if (c != string.charCodeAt(j++)) { + return -1; + } + } + return j; + } + d3_time_format.utc = function(template) { + var local = d3_time_format(template); + function format(date) { + try { + d3_date = d3_date_utc; + var utc = new d3_date(); + utc._ = date; + return local(utc); + } finally { + d3_date = Date; + } + } + format.parse = function(string) { + try { + d3_date = d3_date_utc; + var date = local.parse(string); + return date && date._; + } finally { + d3_date = Date; + } + }; + format.toString = local.toString; + return format; + }; + d3_time_format.multi = d3_time_format.utc.multi = d3_time_formatMulti; + var d3_time_periodLookup = d3.map(), d3_time_dayRe = d3_time_formatRe(locale_days), d3_time_dayLookup = d3_time_formatLookup(locale_days), d3_time_dayAbbrevRe = d3_time_formatRe(locale_shortDays), d3_time_dayAbbrevLookup = d3_time_formatLookup(locale_shortDays), d3_time_monthRe = d3_time_formatRe(locale_months), d3_time_monthLookup = d3_time_formatLookup(locale_months), d3_time_monthAbbrevRe = d3_time_formatRe(locale_shortMonths), d3_time_monthAbbrevLookup = d3_time_formatLookup(locale_shortMonths); + locale_periods.forEach(function(p, i) { + d3_time_periodLookup.set(p.toLowerCase(), i); + }); + var d3_time_formats = { + a: function(d) { + return locale_shortDays[d.getDay()]; + }, + A: function(d) { + return locale_days[d.getDay()]; + }, + b: function(d) { + return locale_shortMonths[d.getMonth()]; + }, + B: function(d) { + return locale_months[d.getMonth()]; + }, + c: d3_time_format(locale_dateTime), + d: function(d, p) { + return d3_time_formatPad(d.getDate(), p, 2); + }, + e: function(d, p) { + return d3_time_formatPad(d.getDate(), p, 2); + }, + H: function(d, p) { + return d3_time_formatPad(d.getHours(), p, 2); + }, + I: function(d, p) { + return d3_time_formatPad(d.getHours() % 12 || 12, p, 2); + }, + j: function(d, p) { + return d3_time_formatPad(1 + d3_time.dayOfYear(d), p, 3); + }, + L: function(d, p) { + return d3_time_formatPad(d.getMilliseconds(), p, 3); + }, + m: function(d, p) { + return d3_time_formatPad(d.getMonth() + 1, p, 2); + }, + M: function(d, p) { + return d3_time_formatPad(d.getMinutes(), p, 2); + }, + p: function(d) { + return locale_periods[+(d.getHours() >= 12)]; + }, + S: function(d, p) { + return d3_time_formatPad(d.getSeconds(), p, 2); + }, + U: function(d, p) { + return d3_time_formatPad(d3_time.sundayOfYear(d), p, 2); + }, + w: function(d) { + return d.getDay(); + }, + W: function(d, p) { + return d3_time_formatPad(d3_time.mondayOfYear(d), p, 2); + }, + x: d3_time_format(locale_date), + X: d3_time_format(locale_time), + y: function(d, p) { + return d3_time_formatPad(d.getFullYear() % 100, p, 2); + }, + Y: function(d, p) { + return d3_time_formatPad(d.getFullYear() % 1e4, p, 4); + }, + Z: d3_time_zone, + "%": function() { + return "%"; + } + }; + var d3_time_parsers = { + a: d3_time_parseWeekdayAbbrev, + A: d3_time_parseWeekday, + b: d3_time_parseMonthAbbrev, + B: d3_time_parseMonth, + c: d3_time_parseLocaleFull, + d: d3_time_parseDay, + e: d3_time_parseDay, + H: d3_time_parseHour24, + I: d3_time_parseHour24, + j: d3_time_parseDayOfYear, + L: d3_time_parseMilliseconds, + m: d3_time_parseMonthNumber, + M: d3_time_parseMinutes, + p: d3_time_parseAmPm, + S: d3_time_parseSeconds, + U: d3_time_parseWeekNumberSunday, + w: d3_time_parseWeekdayNumber, + W: d3_time_parseWeekNumberMonday, + x: d3_time_parseLocaleDate, + X: d3_time_parseLocaleTime, + y: d3_time_parseYear, + Y: d3_time_parseFullYear, + Z: d3_time_parseZone, + "%": d3_time_parseLiteralPercent + }; + function d3_time_parseWeekdayAbbrev(date, string, i) { + d3_time_dayAbbrevRe.lastIndex = 0; + var n = d3_time_dayAbbrevRe.exec(string.substring(i)); + return n ? (date.w = d3_time_dayAbbrevLookup.get(n[0].toLowerCase()), i + n[0].length) : -1; + } + function d3_time_parseWeekday(date, string, i) { + d3_time_dayRe.lastIndex = 0; + var n = d3_time_dayRe.exec(string.substring(i)); + return n ? (date.w = d3_time_dayLookup.get(n[0].toLowerCase()), i + n[0].length) : -1; + } + function d3_time_parseMonthAbbrev(date, string, i) { + d3_time_monthAbbrevRe.lastIndex = 0; + var n = d3_time_monthAbbrevRe.exec(string.substring(i)); + return n ? (date.m = d3_time_monthAbbrevLookup.get(n[0].toLowerCase()), i + n[0].length) : -1; + } + function d3_time_parseMonth(date, string, i) { + d3_time_monthRe.lastIndex = 0; + var n = d3_time_monthRe.exec(string.substring(i)); + return n ? (date.m = d3_time_monthLookup.get(n[0].toLowerCase()), i + n[0].length) : -1; + } + function d3_time_parseLocaleFull(date, string, i) { + return d3_time_parse(date, d3_time_formats.c.toString(), string, i); + } + function d3_time_parseLocaleDate(date, string, i) { + return d3_time_parse(date, d3_time_formats.x.toString(), string, i); + } + function d3_time_parseLocaleTime(date, string, i) { + return d3_time_parse(date, d3_time_formats.X.toString(), string, i); + } + function d3_time_parseAmPm(date, string, i) { + var n = d3_time_periodLookup.get(string.substring(i, i += 2).toLowerCase()); + return n == null ? -1 : (date.p = n, i); + } + return d3_time_format; + } + var d3_time_formatPads = { + "-": "", + _: " ", + "0": "0" + }, d3_time_numberRe = /^\s*\d+/, d3_time_percentRe = /^%/; + function d3_time_formatPad(value, fill, width) { + var sign = value < 0 ? "-" : "", string = (sign ? -value : value) + "", length = string.length; + return sign + (length < width ? new Array(width - length + 1).join(fill) + string : string); + } + function d3_time_formatRe(names) { + return new RegExp("^(?:" + names.map(d3.requote).join("|") + ")", "i"); + } + function d3_time_formatLookup(names) { + var map = new d3_Map(), i = -1, n = names.length; + while (++i < n) map.set(names[i].toLowerCase(), i); + return map; + } + function d3_time_parseWeekdayNumber(date, string, i) { + d3_time_numberRe.lastIndex = 0; + var n = d3_time_numberRe.exec(string.substring(i, i + 1)); + return n ? (date.w = +n[0], i + n[0].length) : -1; + } + function d3_time_parseWeekNumberSunday(date, string, i) { + d3_time_numberRe.lastIndex = 0; + var n = d3_time_numberRe.exec(string.substring(i)); + return n ? (date.U = +n[0], i + n[0].length) : -1; + } + function d3_time_parseWeekNumberMonday(date, string, i) { + d3_time_numberRe.lastIndex = 0; + var n = d3_time_numberRe.exec(string.substring(i)); + return n ? (date.W = +n[0], i + n[0].length) : -1; + } + function d3_time_parseFullYear(date, string, i) { + d3_time_numberRe.lastIndex = 0; + var n = d3_time_numberRe.exec(string.substring(i, i + 4)); + return n ? (date.y = +n[0], i + n[0].length) : -1; + } + function d3_time_parseYear(date, string, i) { + d3_time_numberRe.lastIndex = 0; + var n = d3_time_numberRe.exec(string.substring(i, i + 2)); + return n ? (date.y = d3_time_expandYear(+n[0]), i + n[0].length) : -1; + } + function d3_time_parseZone(date, string, i) { + return /^[+-]\d{4}$/.test(string = string.substring(i, i + 5)) ? (date.Z = -string, + i + 5) : -1; + } + function d3_time_expandYear(d) { + return d + (d > 68 ? 1900 : 2e3); + } + function d3_time_parseMonthNumber(date, string, i) { + d3_time_numberRe.lastIndex = 0; + var n = d3_time_numberRe.exec(string.substring(i, i + 2)); + return n ? (date.m = n[0] - 1, i + n[0].length) : -1; + } + function d3_time_parseDay(date, string, i) { + d3_time_numberRe.lastIndex = 0; + var n = d3_time_numberRe.exec(string.substring(i, i + 2)); + return n ? (date.d = +n[0], i + n[0].length) : -1; + } + function d3_time_parseDayOfYear(date, string, i) { + d3_time_numberRe.lastIndex = 0; + var n = d3_time_numberRe.exec(string.substring(i, i + 3)); + return n ? (date.j = +n[0], i + n[0].length) : -1; + } + function d3_time_parseHour24(date, string, i) { + d3_time_numberRe.lastIndex = 0; + var n = d3_time_numberRe.exec(string.substring(i, i + 2)); + return n ? (date.H = +n[0], i + n[0].length) : -1; + } + function d3_time_parseMinutes(date, string, i) { + d3_time_numberRe.lastIndex = 0; + var n = d3_time_numberRe.exec(string.substring(i, i + 2)); + return n ? (date.M = +n[0], i + n[0].length) : -1; + } + function d3_time_parseSeconds(date, string, i) { + d3_time_numberRe.lastIndex = 0; + var n = d3_time_numberRe.exec(string.substring(i, i + 2)); + return n ? (date.S = +n[0], i + n[0].length) : -1; + } + function d3_time_parseMilliseconds(date, string, i) { + d3_time_numberRe.lastIndex = 0; + var n = d3_time_numberRe.exec(string.substring(i, i + 3)); + return n ? (date.L = +n[0], i + n[0].length) : -1; + } + function d3_time_zone(d) { + var z = d.getTimezoneOffset(), zs = z > 0 ? "-" : "+", zh = ~~(abs(z) / 60), zm = abs(z) % 60; + return zs + d3_time_formatPad(zh, "0", 2) + d3_time_formatPad(zm, "0", 2); + } + function d3_time_parseLiteralPercent(date, string, i) { + d3_time_percentRe.lastIndex = 0; + var n = d3_time_percentRe.exec(string.substring(i, i + 1)); + return n ? i + n[0].length : -1; + } + function d3_time_formatMulti(formats) { + var n = formats.length, i = -1; + while (++i < n) formats[i][0] = this(formats[i][0]); + return function(date) { + var i = 0, f = formats[i]; + while (!f[1](date)) f = formats[++i]; + return f[0](date); + }; + } + d3.locale = function(locale) { + return { + numberFormat: d3_locale_numberFormat(locale), + timeFormat: d3_locale_timeFormat(locale) + }; + }; + var d3_locale_enUS = d3.locale({ + decimal: ".", + thousands: ",", + grouping: [ 3 ], + currency: [ "$", "" ], + dateTime: "%a %b %e %X %Y", + date: "%m/%d/%Y", + time: "%H:%M:%S", + periods: [ "AM", "PM" ], + days: [ "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" ], + shortDays: [ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" ], + months: [ "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" ], + shortMonths: [ "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" ] + }); + d3.format = d3_locale_enUS.numberFormat; + d3.geo = {}; + function d3_adder() {} + d3_adder.prototype = { + s: 0, + t: 0, + add: function(y) { + d3_adderSum(y, this.t, d3_adderTemp); + d3_adderSum(d3_adderTemp.s, this.s, this); + if (this.s) this.t += d3_adderTemp.t; else this.s = d3_adderTemp.t; + }, + reset: function() { + this.s = this.t = 0; + }, + valueOf: function() { + return this.s; + } + }; + var d3_adderTemp = new d3_adder(); + function d3_adderSum(a, b, o) { + var x = o.s = a + b, bv = x - a, av = x - bv; + o.t = a - av + (b - bv); + } + d3.geo.stream = function(object, listener) { + if (object && d3_geo_streamObjectType.hasOwnProperty(object.type)) { + d3_geo_streamObjectType[object.type](object, listener); + } else { + d3_geo_streamGeometry(object, listener); + } + }; + function d3_geo_streamGeometry(geometry, listener) { + if (geometry && d3_geo_streamGeometryType.hasOwnProperty(geometry.type)) { + d3_geo_streamGeometryType[geometry.type](geometry, listener); + } + } + var d3_geo_streamObjectType = { + Feature: function(feature, listener) { + d3_geo_streamGeometry(feature.geometry, listener); + }, + FeatureCollection: function(object, listener) { + var features = object.features, i = -1, n = features.length; + while (++i < n) d3_geo_streamGeometry(features[i].geometry, listener); + } + }; + var d3_geo_streamGeometryType = { + Sphere: function(object, listener) { + listener.sphere(); + }, + Point: function(object, listener) { + object = object.coordinates; + listener.point(object[0], object[1], object[2]); + }, + MultiPoint: function(object, listener) { + var coordinates = object.coordinates, i = -1, n = coordinates.length; + while (++i < n) object = coordinates[i], listener.point(object[0], object[1], object[2]); + }, + LineString: function(object, listener) { + d3_geo_streamLine(object.coordinates, listener, 0); + }, + MultiLineString: function(object, listener) { + var coordinates = object.coordinates, i = -1, n = coordinates.length; + while (++i < n) d3_geo_streamLine(coordinates[i], listener, 0); + }, + Polygon: function(object, listener) { + d3_geo_streamPolygon(object.coordinates, listener); + }, + MultiPolygon: function(object, listener) { + var coordinates = object.coordinates, i = -1, n = coordinates.length; + while (++i < n) d3_geo_streamPolygon(coordinates[i], listener); + }, + GeometryCollection: function(object, listener) { + var geometries = object.geometries, i = -1, n = geometries.length; + while (++i < n) d3_geo_streamGeometry(geometries[i], listener); + } + }; + function d3_geo_streamLine(coordinates, listener, closed) { + var i = -1, n = coordinates.length - closed, coordinate; + listener.lineStart(); + while (++i < n) coordinate = coordinates[i], listener.point(coordinate[0], coordinate[1], coordinate[2]); + listener.lineEnd(); + } + function d3_geo_streamPolygon(coordinates, listener) { + var i = -1, n = coordinates.length; + listener.polygonStart(); + while (++i < n) d3_geo_streamLine(coordinates[i], listener, 1); + listener.polygonEnd(); + } + d3.geo.area = function(object) { + d3_geo_areaSum = 0; + d3.geo.stream(object, d3_geo_area); + return d3_geo_areaSum; + }; + var d3_geo_areaSum, d3_geo_areaRingSum = new d3_adder(); + var d3_geo_area = { + sphere: function() { + d3_geo_areaSum += 4 * π; + }, + point: d3_noop, + lineStart: d3_noop, + lineEnd: d3_noop, + polygonStart: function() { + d3_geo_areaRingSum.reset(); + d3_geo_area.lineStart = d3_geo_areaRingStart; + }, + polygonEnd: function() { + var area = 2 * d3_geo_areaRingSum; + d3_geo_areaSum += area < 0 ? 4 * π + area : area; + d3_geo_area.lineStart = d3_geo_area.lineEnd = d3_geo_area.point = d3_noop; + } + }; + function d3_geo_areaRingStart() { + var λ00, φ00, λ0, cosφ0, sinφ0; + d3_geo_area.point = function(λ, φ) { + d3_geo_area.point = nextPoint; + λ0 = (λ00 = λ) * d3_radians, cosφ0 = Math.cos(φ = (φ00 = φ) * d3_radians / 2 + π / 4), + sinφ0 = Math.sin(φ); + }; + function nextPoint(λ, φ) { + λ *= d3_radians; + φ = φ * d3_radians / 2 + π / 4; + var dλ = λ - λ0, sdλ = dλ >= 0 ? 1 : -1, adλ = sdλ * dλ, cosφ = Math.cos(φ), sinφ = Math.sin(φ), k = sinφ0 * sinφ, u = cosφ0 * cosφ + k * Math.cos(adλ), v = k * sdλ * Math.sin(adλ); + d3_geo_areaRingSum.add(Math.atan2(v, u)); + λ0 = λ, cosφ0 = cosφ, sinφ0 = sinφ; + } + d3_geo_area.lineEnd = function() { + nextPoint(λ00, φ00); + }; + } + function d3_geo_cartesian(spherical) { + var λ = spherical[0], φ = spherical[1], cosφ = Math.cos(φ); + return [ cosφ * Math.cos(λ), cosφ * Math.sin(λ), Math.sin(φ) ]; + } + function d3_geo_cartesianDot(a, b) { + return a[0] * b[0] + a[1] * b[1] + a[2] * b[2]; + } + function d3_geo_cartesianCross(a, b) { + return [ a[1] * b[2] - a[2] * b[1], a[2] * b[0] - a[0] * b[2], a[0] * b[1] - a[1] * b[0] ]; + } + function d3_geo_cartesianAdd(a, b) { + a[0] += b[0]; + a[1] += b[1]; + a[2] += b[2]; + } + function d3_geo_cartesianScale(vector, k) { + return [ vector[0] * k, vector[1] * k, vector[2] * k ]; + } + function d3_geo_cartesianNormalize(d) { + var l = Math.sqrt(d[0] * d[0] + d[1] * d[1] + d[2] * d[2]); + d[0] /= l; + d[1] /= l; + d[2] /= l; + } + function d3_geo_spherical(cartesian) { + return [ Math.atan2(cartesian[1], cartesian[0]), d3_asin(cartesian[2]) ]; + } + function d3_geo_sphericalEqual(a, b) { + return abs(a[0] - b[0]) < ε && abs(a[1] - b[1]) < ε; + } + d3.geo.bounds = function() { + var λ0, φ0, λ1, φ1, λ_, λ__, φ__, p0, dλSum, ranges, range; + var bound = { + point: point, + lineStart: lineStart, + lineEnd: lineEnd, + polygonStart: function() { + bound.point = ringPoint; + bound.lineStart = ringStart; + bound.lineEnd = ringEnd; + dλSum = 0; + d3_geo_area.polygonStart(); + }, + polygonEnd: function() { + d3_geo_area.polygonEnd(); + bound.point = point; + bound.lineStart = lineStart; + bound.lineEnd = lineEnd; + if (d3_geo_areaRingSum < 0) λ0 = -(λ1 = 180), φ0 = -(φ1 = 90); else if (dλSum > ε) φ1 = 90; else if (dλSum < -ε) φ0 = -90; + range[0] = λ0, range[1] = λ1; + } + }; + function point(λ, φ) { + ranges.push(range = [ λ0 = λ, λ1 = λ ]); + if (φ < φ0) φ0 = φ; + if (φ > φ1) φ1 = φ; + } + function linePoint(λ, φ) { + var p = d3_geo_cartesian([ λ * d3_radians, φ * d3_radians ]); + if (p0) { + var normal = d3_geo_cartesianCross(p0, p), equatorial = [ normal[1], -normal[0], 0 ], inflection = d3_geo_cartesianCross(equatorial, normal); + d3_geo_cartesianNormalize(inflection); + inflection = d3_geo_spherical(inflection); + var dλ = λ - λ_, s = dλ > 0 ? 1 : -1, λi = inflection[0] * d3_degrees * s, antimeridian = abs(dλ) > 180; + if (antimeridian ^ (s * λ_ < λi && λi < s * λ)) { + var φi = inflection[1] * d3_degrees; + if (φi > φ1) φ1 = φi; + } else if (λi = (λi + 360) % 360 - 180, antimeridian ^ (s * λ_ < λi && λi < s * λ)) { + var φi = -inflection[1] * d3_degrees; + if (φi < φ0) φ0 = φi; + } else { + if (φ < φ0) φ0 = φ; + if (φ > φ1) φ1 = φ; + } + if (antimeridian) { + if (λ < λ_) { + if (angle(λ0, λ) > angle(λ0, λ1)) λ1 = λ; + } else { + if (angle(λ, λ1) > angle(λ0, λ1)) λ0 = λ; + } + } else { + if (λ1 >= λ0) { + if (λ < λ0) λ0 = λ; + if (λ > λ1) λ1 = λ; + } else { + if (λ > λ_) { + if (angle(λ0, λ) > angle(λ0, λ1)) λ1 = λ; + } else { + if (angle(λ, λ1) > angle(λ0, λ1)) λ0 = λ; + } + } + } + } else { + point(λ, φ); + } + p0 = p, λ_ = λ; + } + function lineStart() { + bound.point = linePoint; + } + function lineEnd() { + range[0] = λ0, range[1] = λ1; + bound.point = point; + p0 = null; + } + function ringPoint(λ, φ) { + if (p0) { + var dλ = λ - λ_; + dλSum += abs(dλ) > 180 ? dλ + (dλ > 0 ? 360 : -360) : dλ; + } else λ__ = λ, φ__ = φ; + d3_geo_area.point(λ, φ); + linePoint(λ, φ); + } + function ringStart() { + d3_geo_area.lineStart(); + } + function ringEnd() { + ringPoint(λ__, φ__); + d3_geo_area.lineEnd(); + if (abs(dλSum) > ε) λ0 = -(λ1 = 180); + range[0] = λ0, range[1] = λ1; + p0 = null; + } + function angle(λ0, λ1) { + return (λ1 -= λ0) < 0 ? λ1 + 360 : λ1; + } + function compareRanges(a, b) { + return a[0] - b[0]; + } + function withinRange(x, range) { + return range[0] <= range[1] ? range[0] <= x && x <= range[1] : x < range[0] || range[1] < x; + } + return function(feature) { + φ1 = λ1 = -(λ0 = φ0 = Infinity); + ranges = []; + d3.geo.stream(feature, bound); + var n = ranges.length; + if (n) { + ranges.sort(compareRanges); + for (var i = 1, a = ranges[0], b, merged = [ a ]; i < n; ++i) { + b = ranges[i]; + if (withinRange(b[0], a) || withinRange(b[1], a)) { + if (angle(a[0], b[1]) > angle(a[0], a[1])) a[1] = b[1]; + if (angle(b[0], a[1]) > angle(a[0], a[1])) a[0] = b[0]; + } else { + merged.push(a = b); + } + } + var best = -Infinity, dλ; + for (var n = merged.length - 1, i = 0, a = merged[n], b; i <= n; a = b, ++i) { + b = merged[i]; + if ((dλ = angle(a[1], b[0])) > best) best = dλ, λ0 = b[0], λ1 = a[1]; + } + } + ranges = range = null; + return λ0 === Infinity || φ0 === Infinity ? [ [ NaN, NaN ], [ NaN, NaN ] ] : [ [ λ0, φ0 ], [ λ1, φ1 ] ]; + }; + }(); + d3.geo.centroid = function(object) { + d3_geo_centroidW0 = d3_geo_centroidW1 = d3_geo_centroidX0 = d3_geo_centroidY0 = d3_geo_centroidZ0 = d3_geo_centroidX1 = d3_geo_centroidY1 = d3_geo_centroidZ1 = d3_geo_centroidX2 = d3_geo_centroidY2 = d3_geo_centroidZ2 = 0; + d3.geo.stream(object, d3_geo_centroid); + var x = d3_geo_centroidX2, y = d3_geo_centroidY2, z = d3_geo_centroidZ2, m = x * x + y * y + z * z; + if (m < ε2) { + x = d3_geo_centroidX1, y = d3_geo_centroidY1, z = d3_geo_centroidZ1; + if (d3_geo_centroidW1 < ε) x = d3_geo_centroidX0, y = d3_geo_centroidY0, z = d3_geo_centroidZ0; + m = x * x + y * y + z * z; + if (m < ε2) return [ NaN, NaN ]; + } + return [ Math.atan2(y, x) * d3_degrees, d3_asin(z / Math.sqrt(m)) * d3_degrees ]; + }; + var d3_geo_centroidW0, d3_geo_centroidW1, d3_geo_centroidX0, d3_geo_centroidY0, d3_geo_centroidZ0, d3_geo_centroidX1, d3_geo_centroidY1, d3_geo_centroidZ1, d3_geo_centroidX2, d3_geo_centroidY2, d3_geo_centroidZ2; + var d3_geo_centroid = { + sphere: d3_noop, + point: d3_geo_centroidPoint, + lineStart: d3_geo_centroidLineStart, + lineEnd: d3_geo_centroidLineEnd, + polygonStart: function() { + d3_geo_centroid.lineStart = d3_geo_centroidRingStart; + }, + polygonEnd: function() { + d3_geo_centroid.lineStart = d3_geo_centroidLineStart; + } + }; + function d3_geo_centroidPoint(λ, φ) { + λ *= d3_radians; + var cosφ = Math.cos(φ *= d3_radians); + d3_geo_centroidPointXYZ(cosφ * Math.cos(λ), cosφ * Math.sin(λ), Math.sin(φ)); + } + function d3_geo_centroidPointXYZ(x, y, z) { + ++d3_geo_centroidW0; + d3_geo_centroidX0 += (x - d3_geo_centroidX0) / d3_geo_centroidW0; + d3_geo_centroidY0 += (y - d3_geo_centroidY0) / d3_geo_centroidW0; + d3_geo_centroidZ0 += (z - d3_geo_centroidZ0) / d3_geo_centroidW0; + } + function d3_geo_centroidLineStart() { + var x0, y0, z0; + d3_geo_centroid.point = function(λ, φ) { + λ *= d3_radians; + var cosφ = Math.cos(φ *= d3_radians); + x0 = cosφ * Math.cos(λ); + y0 = cosφ * Math.sin(λ); + z0 = Math.sin(φ); + d3_geo_centroid.point = nextPoint; + d3_geo_centroidPointXYZ(x0, y0, z0); + }; + function nextPoint(λ, φ) { + λ *= d3_radians; + var cosφ = Math.cos(φ *= d3_radians), x = cosφ * Math.cos(λ), y = cosφ * Math.sin(λ), z = Math.sin(φ), w = Math.atan2(Math.sqrt((w = y0 * z - z0 * y) * w + (w = z0 * x - x0 * z) * w + (w = x0 * y - y0 * x) * w), x0 * x + y0 * y + z0 * z); + d3_geo_centroidW1 += w; + d3_geo_centroidX1 += w * (x0 + (x0 = x)); + d3_geo_centroidY1 += w * (y0 + (y0 = y)); + d3_geo_centroidZ1 += w * (z0 + (z0 = z)); + d3_geo_centroidPointXYZ(x0, y0, z0); + } + } + function d3_geo_centroidLineEnd() { + d3_geo_centroid.point = d3_geo_centroidPoint; + } + function d3_geo_centroidRingStart() { + var λ00, φ00, x0, y0, z0; + d3_geo_centroid.point = function(λ, φ) { + λ00 = λ, φ00 = φ; + d3_geo_centroid.point = nextPoint; + λ *= d3_radians; + var cosφ = Math.cos(φ *= d3_radians); + x0 = cosφ * Math.cos(λ); + y0 = cosφ * Math.sin(λ); + z0 = Math.sin(φ); + d3_geo_centroidPointXYZ(x0, y0, z0); + }; + d3_geo_centroid.lineEnd = function() { + nextPoint(λ00, φ00); + d3_geo_centroid.lineEnd = d3_geo_centroidLineEnd; + d3_geo_centroid.point = d3_geo_centroidPoint; + }; + function nextPoint(λ, φ) { + λ *= d3_radians; + var cosφ = Math.cos(φ *= d3_radians), x = cosφ * Math.cos(λ), y = cosφ * Math.sin(λ), z = Math.sin(φ), cx = y0 * z - z0 * y, cy = z0 * x - x0 * z, cz = x0 * y - y0 * x, m = Math.sqrt(cx * cx + cy * cy + cz * cz), u = x0 * x + y0 * y + z0 * z, v = m && -d3_acos(u) / m, w = Math.atan2(m, u); + d3_geo_centroidX2 += v * cx; + d3_geo_centroidY2 += v * cy; + d3_geo_centroidZ2 += v * cz; + d3_geo_centroidW1 += w; + d3_geo_centroidX1 += w * (x0 + (x0 = x)); + d3_geo_centroidY1 += w * (y0 + (y0 = y)); + d3_geo_centroidZ1 += w * (z0 + (z0 = z)); + d3_geo_centroidPointXYZ(x0, y0, z0); + } + } + function d3_true() { + return true; + } + function d3_geo_clipPolygon(segments, compare, clipStartInside, interpolate, listener) { + var subject = [], clip = []; + segments.forEach(function(segment) { + if ((n = segment.length - 1) <= 0) return; + var n, p0 = segment[0], p1 = segment[n]; + if (d3_geo_sphericalEqual(p0, p1)) { + listener.lineStart(); + for (var i = 0; i < n; ++i) listener.point((p0 = segment[i])[0], p0[1]); + listener.lineEnd(); + return; + } + var a = new d3_geo_clipPolygonIntersection(p0, segment, null, true), b = new d3_geo_clipPolygonIntersection(p0, null, a, false); + a.o = b; + subject.push(a); + clip.push(b); + a = new d3_geo_clipPolygonIntersection(p1, segment, null, false); + b = new d3_geo_clipPolygonIntersection(p1, null, a, true); + a.o = b; + subject.push(a); + clip.push(b); + }); + clip.sort(compare); + d3_geo_clipPolygonLinkCircular(subject); + d3_geo_clipPolygonLinkCircular(clip); + if (!subject.length) return; + for (var i = 0, entry = clipStartInside, n = clip.length; i < n; ++i) { + clip[i].e = entry = !entry; + } + var start = subject[0], points, point; + while (1) { + var current = start, isSubject = true; + while (current.v) if ((current = current.n) === start) return; + points = current.z; + listener.lineStart(); + do { + current.v = current.o.v = true; + if (current.e) { + if (isSubject) { + for (var i = 0, n = points.length; i < n; ++i) listener.point((point = points[i])[0], point[1]); + } else { + interpolate(current.x, current.n.x, 1, listener); + } + current = current.n; + } else { + if (isSubject) { + points = current.p.z; + for (var i = points.length - 1; i >= 0; --i) listener.point((point = points[i])[0], point[1]); + } else { + interpolate(current.x, current.p.x, -1, listener); + } + current = current.p; + } + current = current.o; + points = current.z; + isSubject = !isSubject; + } while (!current.v); + listener.lineEnd(); + } + } + function d3_geo_clipPolygonLinkCircular(array) { + if (!(n = array.length)) return; + var n, i = 0, a = array[0], b; + while (++i < n) { + a.n = b = array[i]; + b.p = a; + a = b; + } + a.n = b = array[0]; + b.p = a; + } + function d3_geo_clipPolygonIntersection(point, points, other, entry) { + this.x = point; + this.z = points; + this.o = other; + this.e = entry; + this.v = false; + this.n = this.p = null; + } + function d3_geo_clip(pointVisible, clipLine, interpolate, clipStart) { + return function(rotate, listener) { + var line = clipLine(listener), rotatedClipStart = rotate.invert(clipStart[0], clipStart[1]); + var clip = { + point: point, + lineStart: lineStart, + lineEnd: lineEnd, + polygonStart: function() { + clip.point = pointRing; + clip.lineStart = ringStart; + clip.lineEnd = ringEnd; + segments = []; + polygon = []; + }, + polygonEnd: function() { + clip.point = point; + clip.lineStart = lineStart; + clip.lineEnd = lineEnd; + segments = d3.merge(segments); + var clipStartInside = d3_geo_pointInPolygon(rotatedClipStart, polygon); + if (segments.length) { + if (!polygonStarted) listener.polygonStart(), polygonStarted = true; + d3_geo_clipPolygon(segments, d3_geo_clipSort, clipStartInside, interpolate, listener); + } else if (clipStartInside) { + if (!polygonStarted) listener.polygonStart(), polygonStarted = true; + listener.lineStart(); + interpolate(null, null, 1, listener); + listener.lineEnd(); + } + if (polygonStarted) listener.polygonEnd(), polygonStarted = false; + segments = polygon = null; + }, + sphere: function() { + listener.polygonStart(); + listener.lineStart(); + interpolate(null, null, 1, listener); + listener.lineEnd(); + listener.polygonEnd(); + } + }; + function point(λ, φ) { + var point = rotate(λ, φ); + if (pointVisible(λ = point[0], φ = point[1])) listener.point(λ, φ); + } + function pointLine(λ, φ) { + var point = rotate(λ, φ); + line.point(point[0], point[1]); + } + function lineStart() { + clip.point = pointLine; + line.lineStart(); + } + function lineEnd() { + clip.point = point; + line.lineEnd(); + } + var segments; + var buffer = d3_geo_clipBufferListener(), ringListener = clipLine(buffer), polygonStarted = false, polygon, ring; + function pointRing(λ, φ) { + ring.push([ λ, φ ]); + var point = rotate(λ, φ); + ringListener.point(point[0], point[1]); + } + function ringStart() { + ringListener.lineStart(); + ring = []; + } + function ringEnd() { + pointRing(ring[0][0], ring[0][1]); + ringListener.lineEnd(); + var clean = ringListener.clean(), ringSegments = buffer.buffer(), segment, n = ringSegments.length; + ring.pop(); + polygon.push(ring); + ring = null; + if (!n) return; + if (clean & 1) { + segment = ringSegments[0]; + var n = segment.length - 1, i = -1, point; + if (n > 0) { + if (!polygonStarted) listener.polygonStart(), polygonStarted = true; + listener.lineStart(); + while (++i < n) listener.point((point = segment[i])[0], point[1]); + listener.lineEnd(); + } + return; + } + if (n > 1 && clean & 2) ringSegments.push(ringSegments.pop().concat(ringSegments.shift())); + segments.push(ringSegments.filter(d3_geo_clipSegmentLength1)); + } + return clip; + }; + } + function d3_geo_clipSegmentLength1(segment) { + return segment.length > 1; + } + function d3_geo_clipBufferListener() { + var lines = [], line; + return { + lineStart: function() { + lines.push(line = []); + }, + point: function(λ, φ) { + line.push([ λ, φ ]); + }, + lineEnd: d3_noop, + buffer: function() { + var buffer = lines; + lines = []; + line = null; + return buffer; + }, + rejoin: function() { + if (lines.length > 1) lines.push(lines.pop().concat(lines.shift())); + } + }; + } + function d3_geo_clipSort(a, b) { + return ((a = a.x)[0] < 0 ? a[1] - halfπ - ε : halfπ - a[1]) - ((b = b.x)[0] < 0 ? b[1] - halfπ - ε : halfπ - b[1]); + } + function d3_geo_pointInPolygon(point, polygon) { + var meridian = point[0], parallel = point[1], meridianNormal = [ Math.sin(meridian), -Math.cos(meridian), 0 ], polarAngle = 0, winding = 0; + d3_geo_areaRingSum.reset(); + for (var i = 0, n = polygon.length; i < n; ++i) { + var ring = polygon[i], m = ring.length; + if (!m) continue; + var point0 = ring[0], λ0 = point0[0], φ0 = point0[1] / 2 + π / 4, sinφ0 = Math.sin(φ0), cosφ0 = Math.cos(φ0), j = 1; + while (true) { + if (j === m) j = 0; + point = ring[j]; + var λ = point[0], φ = point[1] / 2 + π / 4, sinφ = Math.sin(φ), cosφ = Math.cos(φ), dλ = λ - λ0, sdλ = dλ >= 0 ? 1 : -1, adλ = sdλ * dλ, antimeridian = adλ > π, k = sinφ0 * sinφ; + d3_geo_areaRingSum.add(Math.atan2(k * sdλ * Math.sin(adλ), cosφ0 * cosφ + k * Math.cos(adλ))); + polarAngle += antimeridian ? dλ + sdλ * τ : dλ; + if (antimeridian ^ λ0 >= meridian ^ λ >= meridian) { + var arc = d3_geo_cartesianCross(d3_geo_cartesian(point0), d3_geo_cartesian(point)); + d3_geo_cartesianNormalize(arc); + var intersection = d3_geo_cartesianCross(meridianNormal, arc); + d3_geo_cartesianNormalize(intersection); + var φarc = (antimeridian ^ dλ >= 0 ? -1 : 1) * d3_asin(intersection[2]); + if (parallel > φarc || parallel === φarc && (arc[0] || arc[1])) { + winding += antimeridian ^ dλ >= 0 ? 1 : -1; + } + } + if (!j++) break; + λ0 = λ, sinφ0 = sinφ, cosφ0 = cosφ, point0 = point; + } + } + return (polarAngle < -ε || polarAngle < ε && d3_geo_areaRingSum < 0) ^ winding & 1; + } + var d3_geo_clipAntimeridian = d3_geo_clip(d3_true, d3_geo_clipAntimeridianLine, d3_geo_clipAntimeridianInterpolate, [ -π, -π / 2 ]); + function d3_geo_clipAntimeridianLine(listener) { + var λ0 = NaN, φ0 = NaN, sλ0 = NaN, clean; + return { + lineStart: function() { + listener.lineStart(); + clean = 1; + }, + point: function(λ1, φ1) { + var sλ1 = λ1 > 0 ? π : -π, dλ = abs(λ1 - λ0); + if (abs(dλ - π) < ε) { + listener.point(λ0, φ0 = (φ0 + φ1) / 2 > 0 ? halfπ : -halfπ); + listener.point(sλ0, φ0); + listener.lineEnd(); + listener.lineStart(); + listener.point(sλ1, φ0); + listener.point(λ1, φ0); + clean = 0; + } else if (sλ0 !== sλ1 && dλ >= π) { + if (abs(λ0 - sλ0) < ε) λ0 -= sλ0 * ε; + if (abs(λ1 - sλ1) < ε) λ1 -= sλ1 * ε; + φ0 = d3_geo_clipAntimeridianIntersect(λ0, φ0, λ1, φ1); + listener.point(sλ0, φ0); + listener.lineEnd(); + listener.lineStart(); + listener.point(sλ1, φ0); + clean = 0; + } + listener.point(λ0 = λ1, φ0 = φ1); + sλ0 = sλ1; + }, + lineEnd: function() { + listener.lineEnd(); + λ0 = φ0 = NaN; + }, + clean: function() { + return 2 - clean; + } + }; + } + function d3_geo_clipAntimeridianIntersect(λ0, φ0, λ1, φ1) { + var cosφ0, cosφ1, sinλ0_λ1 = Math.sin(λ0 - λ1); + return abs(sinλ0_λ1) > ε ? Math.atan((Math.sin(φ0) * (cosφ1 = Math.cos(φ1)) * Math.sin(λ1) - Math.sin(φ1) * (cosφ0 = Math.cos(φ0)) * Math.sin(λ0)) / (cosφ0 * cosφ1 * sinλ0_λ1)) : (φ0 + φ1) / 2; + } + function d3_geo_clipAntimeridianInterpolate(from, to, direction, listener) { + var φ; + if (from == null) { + φ = direction * halfπ; + listener.point(-π, φ); + listener.point(0, φ); + listener.point(π, φ); + listener.point(π, 0); + listener.point(π, -φ); + listener.point(0, -φ); + listener.point(-π, -φ); + listener.point(-π, 0); + listener.point(-π, φ); + } else if (abs(from[0] - to[0]) > ε) { + var s = from[0] < to[0] ? π : -π; + φ = direction * s / 2; + listener.point(-s, φ); + listener.point(0, φ); + listener.point(s, φ); + } else { + listener.point(to[0], to[1]); + } + } + function d3_geo_clipCircle(radius) { + var cr = Math.cos(radius), smallRadius = cr > 0, notHemisphere = abs(cr) > ε, interpolate = d3_geo_circleInterpolate(radius, 6 * d3_radians); + return d3_geo_clip(visible, clipLine, interpolate, smallRadius ? [ 0, -radius ] : [ -π, radius - π ]); + function visible(λ, φ) { + return Math.cos(λ) * Math.cos(φ) > cr; + } + function clipLine(listener) { + var point0, c0, v0, v00, clean; + return { + lineStart: function() { + v00 = v0 = false; + clean = 1; + }, + point: function(λ, φ) { + var point1 = [ λ, φ ], point2, v = visible(λ, φ), c = smallRadius ? v ? 0 : code(λ, φ) : v ? code(λ + (λ < 0 ? π : -π), φ) : 0; + if (!point0 && (v00 = v0 = v)) listener.lineStart(); + if (v !== v0) { + point2 = intersect(point0, point1); + if (d3_geo_sphericalEqual(point0, point2) || d3_geo_sphericalEqual(point1, point2)) { + point1[0] += ε; + point1[1] += ε; + v = visible(point1[0], point1[1]); + } + } + if (v !== v0) { + clean = 0; + if (v) { + listener.lineStart(); + point2 = intersect(point1, point0); + listener.point(point2[0], point2[1]); + } else { + point2 = intersect(point0, point1); + listener.point(point2[0], point2[1]); + listener.lineEnd(); + } + point0 = point2; + } else if (notHemisphere && point0 && smallRadius ^ v) { + var t; + if (!(c & c0) && (t = intersect(point1, point0, true))) { + clean = 0; + if (smallRadius) { + listener.lineStart(); + listener.point(t[0][0], t[0][1]); + listener.point(t[1][0], t[1][1]); + listener.lineEnd(); + } else { + listener.point(t[1][0], t[1][1]); + listener.lineEnd(); + listener.lineStart(); + listener.point(t[0][0], t[0][1]); + } + } + } + if (v && (!point0 || !d3_geo_sphericalEqual(point0, point1))) { + listener.point(point1[0], point1[1]); + } + point0 = point1, v0 = v, c0 = c; + }, + lineEnd: function() { + if (v0) listener.lineEnd(); + point0 = null; + }, + clean: function() { + return clean | (v00 && v0) << 1; + } + }; + } + function intersect(a, b, two) { + var pa = d3_geo_cartesian(a), pb = d3_geo_cartesian(b); + var n1 = [ 1, 0, 0 ], n2 = d3_geo_cartesianCross(pa, pb), n2n2 = d3_geo_cartesianDot(n2, n2), n1n2 = n2[0], determinant = n2n2 - n1n2 * n1n2; + if (!determinant) return !two && a; + var c1 = cr * n2n2 / determinant, c2 = -cr * n1n2 / determinant, n1xn2 = d3_geo_cartesianCross(n1, n2), A = d3_geo_cartesianScale(n1, c1), B = d3_geo_cartesianScale(n2, c2); + d3_geo_cartesianAdd(A, B); + var u = n1xn2, w = d3_geo_cartesianDot(A, u), uu = d3_geo_cartesianDot(u, u), t2 = w * w - uu * (d3_geo_cartesianDot(A, A) - 1); + if (t2 < 0) return; + var t = Math.sqrt(t2), q = d3_geo_cartesianScale(u, (-w - t) / uu); + d3_geo_cartesianAdd(q, A); + q = d3_geo_spherical(q); + if (!two) return q; + var λ0 = a[0], λ1 = b[0], φ0 = a[1], φ1 = b[1], z; + if (λ1 < λ0) z = λ0, λ0 = λ1, λ1 = z; + var δλ = λ1 - λ0, polar = abs(δλ - π) < ε, meridian = polar || δλ < ε; + if (!polar && φ1 < φ0) z = φ0, φ0 = φ1, φ1 = z; + if (meridian ? polar ? φ0 + φ1 > 0 ^ q[1] < (abs(q[0] - λ0) < ε ? φ0 : φ1) : φ0 <= q[1] && q[1] <= φ1 : δλ > π ^ (λ0 <= q[0] && q[0] <= λ1)) { + var q1 = d3_geo_cartesianScale(u, (-w + t) / uu); + d3_geo_cartesianAdd(q1, A); + return [ q, d3_geo_spherical(q1) ]; + } + } + function code(λ, φ) { + var r = smallRadius ? radius : π - radius, code = 0; + if (λ < -r) code |= 1; else if (λ > r) code |= 2; + if (φ < -r) code |= 4; else if (φ > r) code |= 8; + return code; + } + } + function d3_geom_clipLine(x0, y0, x1, y1) { + return function(line) { + var a = line.a, b = line.b, ax = a.x, ay = a.y, bx = b.x, by = b.y, t0 = 0, t1 = 1, dx = bx - ax, dy = by - ay, r; + r = x0 - ax; + if (!dx && r > 0) return; + r /= dx; + if (dx < 0) { + if (r < t0) return; + if (r < t1) t1 = r; + } else if (dx > 0) { + if (r > t1) return; + if (r > t0) t0 = r; + } + r = x1 - ax; + if (!dx && r < 0) return; + r /= dx; + if (dx < 0) { + if (r > t1) return; + if (r > t0) t0 = r; + } else if (dx > 0) { + if (r < t0) return; + if (r < t1) t1 = r; + } + r = y0 - ay; + if (!dy && r > 0) return; + r /= dy; + if (dy < 0) { + if (r < t0) return; + if (r < t1) t1 = r; + } else if (dy > 0) { + if (r > t1) return; + if (r > t0) t0 = r; + } + r = y1 - ay; + if (!dy && r < 0) return; + r /= dy; + if (dy < 0) { + if (r > t1) return; + if (r > t0) t0 = r; + } else if (dy > 0) { + if (r < t0) return; + if (r < t1) t1 = r; + } + if (t0 > 0) line.a = { + x: ax + t0 * dx, + y: ay + t0 * dy + }; + if (t1 < 1) line.b = { + x: ax + t1 * dx, + y: ay + t1 * dy + }; + return line; + }; + } + var d3_geo_clipExtentMAX = 1e9; + d3.geo.clipExtent = function() { + var x0, y0, x1, y1, stream, clip, clipExtent = { + stream: function(output) { + if (stream) stream.valid = false; + stream = clip(output); + stream.valid = true; + return stream; + }, + extent: function(_) { + if (!arguments.length) return [ [ x0, y0 ], [ x1, y1 ] ]; + clip = d3_geo_clipExtent(x0 = +_[0][0], y0 = +_[0][1], x1 = +_[1][0], y1 = +_[1][1]); + if (stream) stream.valid = false, stream = null; + return clipExtent; + } + }; + return clipExtent.extent([ [ 0, 0 ], [ 960, 500 ] ]); + }; + function d3_geo_clipExtent(x0, y0, x1, y1) { + return function(listener) { + var listener_ = listener, bufferListener = d3_geo_clipBufferListener(), clipLine = d3_geom_clipLine(x0, y0, x1, y1), segments, polygon, ring; + var clip = { + point: point, + lineStart: lineStart, + lineEnd: lineEnd, + polygonStart: function() { + listener = bufferListener; + segments = []; + polygon = []; + clean = true; + }, + polygonEnd: function() { + listener = listener_; + segments = d3.merge(segments); + var clipStartInside = insidePolygon([ x0, y1 ]), inside = clean && clipStartInside, visible = segments.length; + if (inside || visible) { + listener.polygonStart(); + if (inside) { + listener.lineStart(); + interpolate(null, null, 1, listener); + listener.lineEnd(); + } + if (visible) { + d3_geo_clipPolygon(segments, compare, clipStartInside, interpolate, listener); + } + listener.polygonEnd(); + } + segments = polygon = ring = null; + } + }; + function insidePolygon(p) { + var wn = 0, n = polygon.length, y = p[1]; + for (var i = 0; i < n; ++i) { + for (var j = 1, v = polygon[i], m = v.length, a = v[0], b; j < m; ++j) { + b = v[j]; + if (a[1] <= y) { + if (b[1] > y && d3_cross2d(a, b, p) > 0) ++wn; + } else { + if (b[1] <= y && d3_cross2d(a, b, p) < 0) --wn; + } + a = b; + } + } + return wn !== 0; + } + function interpolate(from, to, direction, listener) { + var a = 0, a1 = 0; + if (from == null || (a = corner(from, direction)) !== (a1 = corner(to, direction)) || comparePoints(from, to) < 0 ^ direction > 0) { + do { + listener.point(a === 0 || a === 3 ? x0 : x1, a > 1 ? y1 : y0); + } while ((a = (a + direction + 4) % 4) !== a1); + } else { + listener.point(to[0], to[1]); + } + } + function pointVisible(x, y) { + return x0 <= x && x <= x1 && y0 <= y && y <= y1; + } + function point(x, y) { + if (pointVisible(x, y)) listener.point(x, y); + } + var x__, y__, v__, x_, y_, v_, first, clean; + function lineStart() { + clip.point = linePoint; + if (polygon) polygon.push(ring = []); + first = true; + v_ = false; + x_ = y_ = NaN; + } + function lineEnd() { + if (segments) { + linePoint(x__, y__); + if (v__ && v_) bufferListener.rejoin(); + segments.push(bufferListener.buffer()); + } + clip.point = point; + if (v_) listener.lineEnd(); + } + function linePoint(x, y) { + x = Math.max(-d3_geo_clipExtentMAX, Math.min(d3_geo_clipExtentMAX, x)); + y = Math.max(-d3_geo_clipExtentMAX, Math.min(d3_geo_clipExtentMAX, y)); + var v = pointVisible(x, y); + if (polygon) ring.push([ x, y ]); + if (first) { + x__ = x, y__ = y, v__ = v; + first = false; + if (v) { + listener.lineStart(); + listener.point(x, y); + } + } else { + if (v && v_) listener.point(x, y); else { + var l = { + a: { + x: x_, + y: y_ + }, + b: { + x: x, + y: y + } + }; + if (clipLine(l)) { + if (!v_) { + listener.lineStart(); + listener.point(l.a.x, l.a.y); + } + listener.point(l.b.x, l.b.y); + if (!v) listener.lineEnd(); + clean = false; + } else if (v) { + listener.lineStart(); + listener.point(x, y); + clean = false; + } + } + } + x_ = x, y_ = y, v_ = v; + } + return clip; + }; + function corner(p, direction) { + return abs(p[0] - x0) < ε ? direction > 0 ? 0 : 3 : abs(p[0] - x1) < ε ? direction > 0 ? 2 : 1 : abs(p[1] - y0) < ε ? direction > 0 ? 1 : 0 : direction > 0 ? 3 : 2; + } + function compare(a, b) { + return comparePoints(a.x, b.x); + } + function comparePoints(a, b) { + var ca = corner(a, 1), cb = corner(b, 1); + return ca !== cb ? ca - cb : ca === 0 ? b[1] - a[1] : ca === 1 ? a[0] - b[0] : ca === 2 ? a[1] - b[1] : b[0] - a[0]; + } + } + function d3_geo_compose(a, b) { + function compose(x, y) { + return x = a(x, y), b(x[0], x[1]); + } + if (a.invert && b.invert) compose.invert = function(x, y) { + return x = b.invert(x, y), x && a.invert(x[0], x[1]); + }; + return compose; + } + function d3_geo_conic(projectAt) { + var φ0 = 0, φ1 = π / 3, m = d3_geo_projectionMutator(projectAt), p = m(φ0, φ1); + p.parallels = function(_) { + if (!arguments.length) return [ φ0 / π * 180, φ1 / π * 180 ]; + return m(φ0 = _[0] * π / 180, φ1 = _[1] * π / 180); + }; + return p; + } + function d3_geo_conicEqualArea(φ0, φ1) { + var sinφ0 = Math.sin(φ0), n = (sinφ0 + Math.sin(φ1)) / 2, C = 1 + sinφ0 * (2 * n - sinφ0), ρ0 = Math.sqrt(C) / n; + function forward(λ, φ) { + var ρ = Math.sqrt(C - 2 * n * Math.sin(φ)) / n; + return [ ρ * Math.sin(λ *= n), ρ0 - ρ * Math.cos(λ) ]; + } + forward.invert = function(x, y) { + var ρ0_y = ρ0 - y; + return [ Math.atan2(x, ρ0_y) / n, d3_asin((C - (x * x + ρ0_y * ρ0_y) * n * n) / (2 * n)) ]; + }; + return forward; + } + (d3.geo.conicEqualArea = function() { + return d3_geo_conic(d3_geo_conicEqualArea); + }).raw = d3_geo_conicEqualArea; + d3.geo.albers = function() { + return d3.geo.conicEqualArea().rotate([ 96, 0 ]).center([ -.6, 38.7 ]).parallels([ 29.5, 45.5 ]).scale(1070); + }; + d3.geo.albersUsa = function() { + var lower48 = d3.geo.albers(); + var alaska = d3.geo.conicEqualArea().rotate([ 154, 0 ]).center([ -2, 58.5 ]).parallels([ 55, 65 ]); + var hawaii = d3.geo.conicEqualArea().rotate([ 157, 0 ]).center([ -3, 19.9 ]).parallels([ 8, 18 ]); + var point, pointStream = { + point: function(x, y) { + point = [ x, y ]; + } + }, lower48Point, alaskaPoint, hawaiiPoint; + function albersUsa(coordinates) { + var x = coordinates[0], y = coordinates[1]; + point = null; + (lower48Point(x, y), point) || (alaskaPoint(x, y), point) || hawaiiPoint(x, y); + return point; + } + albersUsa.invert = function(coordinates) { + var k = lower48.scale(), t = lower48.translate(), x = (coordinates[0] - t[0]) / k, y = (coordinates[1] - t[1]) / k; + return (y >= .12 && y < .234 && x >= -.425 && x < -.214 ? alaska : y >= .166 && y < .234 && x >= -.214 && x < -.115 ? hawaii : lower48).invert(coordinates); + }; + albersUsa.stream = function(stream) { + var lower48Stream = lower48.stream(stream), alaskaStream = alaska.stream(stream), hawaiiStream = hawaii.stream(stream); + return { + point: function(x, y) { + lower48Stream.point(x, y); + alaskaStream.point(x, y); + hawaiiStream.point(x, y); + }, + sphere: function() { + lower48Stream.sphere(); + alaskaStream.sphere(); + hawaiiStream.sphere(); + }, + lineStart: function() { + lower48Stream.lineStart(); + alaskaStream.lineStart(); + hawaiiStream.lineStart(); + }, + lineEnd: function() { + lower48Stream.lineEnd(); + alaskaStream.lineEnd(); + hawaiiStream.lineEnd(); + }, + polygonStart: function() { + lower48Stream.polygonStart(); + alaskaStream.polygonStart(); + hawaiiStream.polygonStart(); + }, + polygonEnd: function() { + lower48Stream.polygonEnd(); + alaskaStream.polygonEnd(); + hawaiiStream.polygonEnd(); + } + }; + }; + albersUsa.precision = function(_) { + if (!arguments.length) return lower48.precision(); + lower48.precision(_); + alaska.precision(_); + hawaii.precision(_); + return albersUsa; + }; + albersUsa.scale = function(_) { + if (!arguments.length) return lower48.scale(); + lower48.scale(_); + alaska.scale(_ * .35); + hawaii.scale(_); + return albersUsa.translate(lower48.translate()); + }; + albersUsa.translate = function(_) { + if (!arguments.length) return lower48.translate(); + var k = lower48.scale(), x = +_[0], y = +_[1]; + lower48Point = lower48.translate(_).clipExtent([ [ x - .455 * k, y - .238 * k ], [ x + .455 * k, y + .238 * k ] ]).stream(pointStream).point; + alaskaPoint = alaska.translate([ x - .307 * k, y + .201 * k ]).clipExtent([ [ x - .425 * k + ε, y + .12 * k + ε ], [ x - .214 * k - ε, y + .234 * k - ε ] ]).stream(pointStream).point; + hawaiiPoint = hawaii.translate([ x - .205 * k, y + .212 * k ]).clipExtent([ [ x - .214 * k + ε, y + .166 * k + ε ], [ x - .115 * k - ε, y + .234 * k - ε ] ]).stream(pointStream).point; + return albersUsa; + }; + return albersUsa.scale(1070); + }; + var d3_geo_pathAreaSum, d3_geo_pathAreaPolygon, d3_geo_pathArea = { + point: d3_noop, + lineStart: d3_noop, + lineEnd: d3_noop, + polygonStart: function() { + d3_geo_pathAreaPolygon = 0; + d3_geo_pathArea.lineStart = d3_geo_pathAreaRingStart; + }, + polygonEnd: function() { + d3_geo_pathArea.lineStart = d3_geo_pathArea.lineEnd = d3_geo_pathArea.point = d3_noop; + d3_geo_pathAreaSum += abs(d3_geo_pathAreaPolygon / 2); + } + }; + function d3_geo_pathAreaRingStart() { + var x00, y00, x0, y0; + d3_geo_pathArea.point = function(x, y) { + d3_geo_pathArea.point = nextPoint; + x00 = x0 = x, y00 = y0 = y; + }; + function nextPoint(x, y) { + d3_geo_pathAreaPolygon += y0 * x - x0 * y; + x0 = x, y0 = y; + } + d3_geo_pathArea.lineEnd = function() { + nextPoint(x00, y00); + }; + } + var d3_geo_pathBoundsX0, d3_geo_pathBoundsY0, d3_geo_pathBoundsX1, d3_geo_pathBoundsY1; + var d3_geo_pathBounds = { + point: d3_geo_pathBoundsPoint, + lineStart: d3_noop, + lineEnd: d3_noop, + polygonStart: d3_noop, + polygonEnd: d3_noop + }; + function d3_geo_pathBoundsPoint(x, y) { + if (x < d3_geo_pathBoundsX0) d3_geo_pathBoundsX0 = x; + if (x > d3_geo_pathBoundsX1) d3_geo_pathBoundsX1 = x; + if (y < d3_geo_pathBoundsY0) d3_geo_pathBoundsY0 = y; + if (y > d3_geo_pathBoundsY1) d3_geo_pathBoundsY1 = y; + } + function d3_geo_pathBuffer() { + var pointCircle = d3_geo_pathBufferCircle(4.5), buffer = []; + var stream = { + point: point, + lineStart: function() { + stream.point = pointLineStart; + }, + lineEnd: lineEnd, + polygonStart: function() { + stream.lineEnd = lineEndPolygon; + }, + polygonEnd: function() { + stream.lineEnd = lineEnd; + stream.point = point; + }, + pointRadius: function(_) { + pointCircle = d3_geo_pathBufferCircle(_); + return stream; + }, + result: function() { + if (buffer.length) { + var result = buffer.join(""); + buffer = []; + return result; + } + } + }; + function point(x, y) { + buffer.push("M", x, ",", y, pointCircle); + } + function pointLineStart(x, y) { + buffer.push("M", x, ",", y); + stream.point = pointLine; + } + function pointLine(x, y) { + buffer.push("L", x, ",", y); + } + function lineEnd() { + stream.point = point; + } + function lineEndPolygon() { + buffer.push("Z"); + } + return stream; + } + function d3_geo_pathBufferCircle(radius) { + return "m0," + radius + "a" + radius + "," + radius + " 0 1,1 0," + -2 * radius + "a" + radius + "," + radius + " 0 1,1 0," + 2 * radius + "z"; + } + var d3_geo_pathCentroid = { + point: d3_geo_pathCentroidPoint, + lineStart: d3_geo_pathCentroidLineStart, + lineEnd: d3_geo_pathCentroidLineEnd, + polygonStart: function() { + d3_geo_pathCentroid.lineStart = d3_geo_pathCentroidRingStart; + }, + polygonEnd: function() { + d3_geo_pathCentroid.point = d3_geo_pathCentroidPoint; + d3_geo_pathCentroid.lineStart = d3_geo_pathCentroidLineStart; + d3_geo_pathCentroid.lineEnd = d3_geo_pathCentroidLineEnd; + } + }; + function d3_geo_pathCentroidPoint(x, y) { + d3_geo_centroidX0 += x; + d3_geo_centroidY0 += y; + ++d3_geo_centroidZ0; + } + function d3_geo_pathCentroidLineStart() { + var x0, y0; + d3_geo_pathCentroid.point = function(x, y) { + d3_geo_pathCentroid.point = nextPoint; + d3_geo_pathCentroidPoint(x0 = x, y0 = y); + }; + function nextPoint(x, y) { + var dx = x - x0, dy = y - y0, z = Math.sqrt(dx * dx + dy * dy); + d3_geo_centroidX1 += z * (x0 + x) / 2; + d3_geo_centroidY1 += z * (y0 + y) / 2; + d3_geo_centroidZ1 += z; + d3_geo_pathCentroidPoint(x0 = x, y0 = y); + } + } + function d3_geo_pathCentroidLineEnd() { + d3_geo_pathCentroid.point = d3_geo_pathCentroidPoint; + } + function d3_geo_pathCentroidRingStart() { + var x00, y00, x0, y0; + d3_geo_pathCentroid.point = function(x, y) { + d3_geo_pathCentroid.point = nextPoint; + d3_geo_pathCentroidPoint(x00 = x0 = x, y00 = y0 = y); + }; + function nextPoint(x, y) { + var dx = x - x0, dy = y - y0, z = Math.sqrt(dx * dx + dy * dy); + d3_geo_centroidX1 += z * (x0 + x) / 2; + d3_geo_centroidY1 += z * (y0 + y) / 2; + d3_geo_centroidZ1 += z; + z = y0 * x - x0 * y; + d3_geo_centroidX2 += z * (x0 + x); + d3_geo_centroidY2 += z * (y0 + y); + d3_geo_centroidZ2 += z * 3; + d3_geo_pathCentroidPoint(x0 = x, y0 = y); + } + d3_geo_pathCentroid.lineEnd = function() { + nextPoint(x00, y00); + }; + } + function d3_geo_pathContext(context) { + var pointRadius = 4.5; + var stream = { + point: point, + lineStart: function() { + stream.point = pointLineStart; + }, + lineEnd: lineEnd, + polygonStart: function() { + stream.lineEnd = lineEndPolygon; + }, + polygonEnd: function() { + stream.lineEnd = lineEnd; + stream.point = point; + }, + pointRadius: function(_) { + pointRadius = _; + return stream; + }, + result: d3_noop + }; + function point(x, y) { + context.moveTo(x, y); + context.arc(x, y, pointRadius, 0, τ); + } + function pointLineStart(x, y) { + context.moveTo(x, y); + stream.point = pointLine; + } + function pointLine(x, y) { + context.lineTo(x, y); + } + function lineEnd() { + stream.point = point; + } + function lineEndPolygon() { + context.closePath(); + } + return stream; + } + function d3_geo_resample(project) { + var δ2 = .5, cosMinDistance = Math.cos(30 * d3_radians), maxDepth = 16; + function resample(stream) { + return (maxDepth ? resampleRecursive : resampleNone)(stream); + } + function resampleNone(stream) { + return d3_geo_transformPoint(stream, function(x, y) { + x = project(x, y); + stream.point(x[0], x[1]); + }); + } + function resampleRecursive(stream) { + var λ00, φ00, x00, y00, a00, b00, c00, λ0, x0, y0, a0, b0, c0; + var resample = { + point: point, + lineStart: lineStart, + lineEnd: lineEnd, + polygonStart: function() { + stream.polygonStart(); + resample.lineStart = ringStart; + }, + polygonEnd: function() { + stream.polygonEnd(); + resample.lineStart = lineStart; + } + }; + function point(x, y) { + x = project(x, y); + stream.point(x[0], x[1]); + } + function lineStart() { + x0 = NaN; + resample.point = linePoint; + stream.lineStart(); + } + function linePoint(λ, φ) { + var c = d3_geo_cartesian([ λ, φ ]), p = project(λ, φ); + resampleLineTo(x0, y0, λ0, a0, b0, c0, x0 = p[0], y0 = p[1], λ0 = λ, a0 = c[0], b0 = c[1], c0 = c[2], maxDepth, stream); + stream.point(x0, y0); + } + function lineEnd() { + resample.point = point; + stream.lineEnd(); + } + function ringStart() { + lineStart(); + resample.point = ringPoint; + resample.lineEnd = ringEnd; + } + function ringPoint(λ, φ) { + linePoint(λ00 = λ, φ00 = φ), x00 = x0, y00 = y0, a00 = a0, b00 = b0, c00 = c0; + resample.point = linePoint; + } + function ringEnd() { + resampleLineTo(x0, y0, λ0, a0, b0, c0, x00, y00, λ00, a00, b00, c00, maxDepth, stream); + resample.lineEnd = lineEnd; + lineEnd(); + } + return resample; + } + function resampleLineTo(x0, y0, λ0, a0, b0, c0, x1, y1, λ1, a1, b1, c1, depth, stream) { + var dx = x1 - x0, dy = y1 - y0, d2 = dx * dx + dy * dy; + if (d2 > 4 * δ2 && depth--) { + var a = a0 + a1, b = b0 + b1, c = c0 + c1, m = Math.sqrt(a * a + b * b + c * c), φ2 = Math.asin(c /= m), λ2 = abs(abs(c) - 1) < ε || abs(λ0 - λ1) < ε ? (λ0 + λ1) / 2 : Math.atan2(b, a), p = project(λ2, φ2), x2 = p[0], y2 = p[1], dx2 = x2 - x0, dy2 = y2 - y0, dz = dy * dx2 - dx * dy2; + if (dz * dz / d2 > δ2 || abs((dx * dx2 + dy * dy2) / d2 - .5) > .3 || a0 * a1 + b0 * b1 + c0 * c1 < cosMinDistance) { + resampleLineTo(x0, y0, λ0, a0, b0, c0, x2, y2, λ2, a /= m, b /= m, c, depth, stream); + stream.point(x2, y2); + resampleLineTo(x2, y2, λ2, a, b, c, x1, y1, λ1, a1, b1, c1, depth, stream); + } + } + } + resample.precision = function(_) { + if (!arguments.length) return Math.sqrt(δ2); + maxDepth = (δ2 = _ * _) > 0 && 16; + return resample; + }; + return resample; + } + d3.geo.path = function() { + var pointRadius = 4.5, projection, context, projectStream, contextStream, cacheStream; + function path(object) { + if (object) { + if (typeof pointRadius === "function") contextStream.pointRadius(+pointRadius.apply(this, arguments)); + if (!cacheStream || !cacheStream.valid) cacheStream = projectStream(contextStream); + d3.geo.stream(object, cacheStream); + } + return contextStream.result(); + } + path.area = function(object) { + d3_geo_pathAreaSum = 0; + d3.geo.stream(object, projectStream(d3_geo_pathArea)); + return d3_geo_pathAreaSum; + }; + path.centroid = function(object) { + d3_geo_centroidX0 = d3_geo_centroidY0 = d3_geo_centroidZ0 = d3_geo_centroidX1 = d3_geo_centroidY1 = d3_geo_centroidZ1 = d3_geo_centroidX2 = d3_geo_centroidY2 = d3_geo_centroidZ2 = 0; + d3.geo.stream(object, projectStream(d3_geo_pathCentroid)); + return d3_geo_centroidZ2 ? [ d3_geo_centroidX2 / d3_geo_centroidZ2, d3_geo_centroidY2 / d3_geo_centroidZ2 ] : d3_geo_centroidZ1 ? [ d3_geo_centroidX1 / d3_geo_centroidZ1, d3_geo_centroidY1 / d3_geo_centroidZ1 ] : d3_geo_centroidZ0 ? [ d3_geo_centroidX0 / d3_geo_centroidZ0, d3_geo_centroidY0 / d3_geo_centroidZ0 ] : [ NaN, NaN ]; + }; + path.bounds = function(object) { + d3_geo_pathBoundsX1 = d3_geo_pathBoundsY1 = -(d3_geo_pathBoundsX0 = d3_geo_pathBoundsY0 = Infinity); + d3.geo.stream(object, projectStream(d3_geo_pathBounds)); + return [ [ d3_geo_pathBoundsX0, d3_geo_pathBoundsY0 ], [ d3_geo_pathBoundsX1, d3_geo_pathBoundsY1 ] ]; + }; + path.projection = function(_) { + if (!arguments.length) return projection; + projectStream = (projection = _) ? _.stream || d3_geo_pathProjectStream(_) : d3_identity; + return reset(); + }; + path.context = function(_) { + if (!arguments.length) return context; + contextStream = (context = _) == null ? new d3_geo_pathBuffer() : new d3_geo_pathContext(_); + if (typeof pointRadius !== "function") contextStream.pointRadius(pointRadius); + return reset(); + }; + path.pointRadius = function(_) { + if (!arguments.length) return pointRadius; + pointRadius = typeof _ === "function" ? _ : (contextStream.pointRadius(+_), +_); + return path; + }; + function reset() { + cacheStream = null; + return path; + } + return path.projection(d3.geo.albersUsa()).context(null); + }; + function d3_geo_pathProjectStream(project) { + var resample = d3_geo_resample(function(x, y) { + return project([ x * d3_degrees, y * d3_degrees ]); + }); + return function(stream) { + return d3_geo_projectionRadians(resample(stream)); + }; + } + d3.geo.transform = function(methods) { + return { + stream: function(stream) { + var transform = new d3_geo_transform(stream); + for (var k in methods) transform[k] = methods[k]; + return transform; + } + }; + }; + function d3_geo_transform(stream) { + this.stream = stream; + } + d3_geo_transform.prototype = { + point: function(x, y) { + this.stream.point(x, y); + }, + sphere: function() { + this.stream.sphere(); + }, + lineStart: function() { + this.stream.lineStart(); + }, + lineEnd: function() { + this.stream.lineEnd(); + }, + polygonStart: function() { + this.stream.polygonStart(); + }, + polygonEnd: function() { + this.stream.polygonEnd(); + } + }; + function d3_geo_transformPoint(stream, point) { + return { + point: point, + sphere: function() { + stream.sphere(); + }, + lineStart: function() { + stream.lineStart(); + }, + lineEnd: function() { + stream.lineEnd(); + }, + polygonStart: function() { + stream.polygonStart(); + }, + polygonEnd: function() { + stream.polygonEnd(); + } + }; + } + d3.geo.projection = d3_geo_projection; + d3.geo.projectionMutator = d3_geo_projectionMutator; + function d3_geo_projection(project) { + return d3_geo_projectionMutator(function() { + return project; + })(); + } + function d3_geo_projectionMutator(projectAt) { + var project, rotate, projectRotate, projectResample = d3_geo_resample(function(x, y) { + x = project(x, y); + return [ x[0] * k + δx, δy - x[1] * k ]; + }), k = 150, x = 480, y = 250, λ = 0, φ = 0, δλ = 0, δφ = 0, δγ = 0, δx, δy, preclip = d3_geo_clipAntimeridian, postclip = d3_identity, clipAngle = null, clipExtent = null, stream; + function projection(point) { + point = projectRotate(point[0] * d3_radians, point[1] * d3_radians); + return [ point[0] * k + δx, δy - point[1] * k ]; + } + function invert(point) { + point = projectRotate.invert((point[0] - δx) / k, (δy - point[1]) / k); + return point && [ point[0] * d3_degrees, point[1] * d3_degrees ]; + } + projection.stream = function(output) { + if (stream) stream.valid = false; + stream = d3_geo_projectionRadians(preclip(rotate, projectResample(postclip(output)))); + stream.valid = true; + return stream; + }; + projection.clipAngle = function(_) { + if (!arguments.length) return clipAngle; + preclip = _ == null ? (clipAngle = _, d3_geo_clipAntimeridian) : d3_geo_clipCircle((clipAngle = +_) * d3_radians); + return invalidate(); + }; + projection.clipExtent = function(_) { + if (!arguments.length) return clipExtent; + clipExtent = _; + postclip = _ ? d3_geo_clipExtent(_[0][0], _[0][1], _[1][0], _[1][1]) : d3_identity; + return invalidate(); + }; + projection.scale = function(_) { + if (!arguments.length) return k; + k = +_; + return reset(); + }; + projection.translate = function(_) { + if (!arguments.length) return [ x, y ]; + x = +_[0]; + y = +_[1]; + return reset(); + }; + projection.center = function(_) { + if (!arguments.length) return [ λ * d3_degrees, φ * d3_degrees ]; + λ = _[0] % 360 * d3_radians; + φ = _[1] % 360 * d3_radians; + return reset(); + }; + projection.rotate = function(_) { + if (!arguments.length) return [ δλ * d3_degrees, δφ * d3_degrees, δγ * d3_degrees ]; + δλ = _[0] % 360 * d3_radians; + δφ = _[1] % 360 * d3_radians; + δγ = _.length > 2 ? _[2] % 360 * d3_radians : 0; + return reset(); + }; + d3.rebind(projection, projectResample, "precision"); + function reset() { + projectRotate = d3_geo_compose(rotate = d3_geo_rotation(δλ, δφ, δγ), project); + var center = project(λ, φ); + δx = x - center[0] * k; + δy = y + center[1] * k; + return invalidate(); + } + function invalidate() { + if (stream) stream.valid = false, stream = null; + return projection; + } + return function() { + project = projectAt.apply(this, arguments); + projection.invert = project.invert && invert; + return reset(); + }; + } + function d3_geo_projectionRadians(stream) { + return d3_geo_transformPoint(stream, function(x, y) { + stream.point(x * d3_radians, y * d3_radians); + }); + } + function d3_geo_equirectangular(λ, φ) { + return [ λ, φ ]; + } + (d3.geo.equirectangular = function() { + return d3_geo_projection(d3_geo_equirectangular); + }).raw = d3_geo_equirectangular.invert = d3_geo_equirectangular; + d3.geo.rotation = function(rotate) { + rotate = d3_geo_rotation(rotate[0] % 360 * d3_radians, rotate[1] * d3_radians, rotate.length > 2 ? rotate[2] * d3_radians : 0); + function forward(coordinates) { + coordinates = rotate(coordinates[0] * d3_radians, coordinates[1] * d3_radians); + return coordinates[0] *= d3_degrees, coordinates[1] *= d3_degrees, coordinates; + } + forward.invert = function(coordinates) { + coordinates = rotate.invert(coordinates[0] * d3_radians, coordinates[1] * d3_radians); + return coordinates[0] *= d3_degrees, coordinates[1] *= d3_degrees, coordinates; + }; + return forward; + }; + function d3_geo_identityRotation(λ, φ) { + return [ λ > π ? λ - τ : λ < -π ? λ + τ : λ, φ ]; + } + d3_geo_identityRotation.invert = d3_geo_equirectangular; + function d3_geo_rotation(δλ, δφ, δγ) { + return δλ ? δφ || δγ ? d3_geo_compose(d3_geo_rotationλ(δλ), d3_geo_rotationφγ(δφ, δγ)) : d3_geo_rotationλ(δλ) : δφ || δγ ? d3_geo_rotationφγ(δφ, δγ) : d3_geo_identityRotation; + } + function d3_geo_forwardRotationλ(δλ) { + return function(λ, φ) { + return λ += δλ, [ λ > π ? λ - τ : λ < -π ? λ + τ : λ, φ ]; + }; + } + function d3_geo_rotationλ(δλ) { + var rotation = d3_geo_forwardRotationλ(δλ); + rotation.invert = d3_geo_forwardRotationλ(-δλ); + return rotation; + } + function d3_geo_rotationφγ(δφ, δγ) { + var cosδφ = Math.cos(δφ), sinδφ = Math.sin(δφ), cosδγ = Math.cos(δγ), sinδγ = Math.sin(δγ); + function rotation(λ, φ) { + var cosφ = Math.cos(φ), x = Math.cos(λ) * cosφ, y = Math.sin(λ) * cosφ, z = Math.sin(φ), k = z * cosδφ + x * sinδφ; + return [ Math.atan2(y * cosδγ - k * sinδγ, x * cosδφ - z * sinδφ), d3_asin(k * cosδγ + y * sinδγ) ]; + } + rotation.invert = function(λ, φ) { + var cosφ = Math.cos(φ), x = Math.cos(λ) * cosφ, y = Math.sin(λ) * cosφ, z = Math.sin(φ), k = z * cosδγ - y * sinδγ; + return [ Math.atan2(y * cosδγ + z * sinδγ, x * cosδφ + k * sinδφ), d3_asin(k * cosδφ - x * sinδφ) ]; + }; + return rotation; + } + d3.geo.circle = function() { + var origin = [ 0, 0 ], angle, precision = 6, interpolate; + function circle() { + var center = typeof origin === "function" ? origin.apply(this, arguments) : origin, rotate = d3_geo_rotation(-center[0] * d3_radians, -center[1] * d3_radians, 0).invert, ring = []; + interpolate(null, null, 1, { + point: function(x, y) { + ring.push(x = rotate(x, y)); + x[0] *= d3_degrees, x[1] *= d3_degrees; + } + }); + return { + type: "Polygon", + coordinates: [ ring ] + }; + } + circle.origin = function(x) { + if (!arguments.length) return origin; + origin = x; + return circle; + }; + circle.angle = function(x) { + if (!arguments.length) return angle; + interpolate = d3_geo_circleInterpolate((angle = +x) * d3_radians, precision * d3_radians); + return circle; + }; + circle.precision = function(_) { + if (!arguments.length) return precision; + interpolate = d3_geo_circleInterpolate(angle * d3_radians, (precision = +_) * d3_radians); + return circle; + }; + return circle.angle(90); + }; + function d3_geo_circleInterpolate(radius, precision) { + var cr = Math.cos(radius), sr = Math.sin(radius); + return function(from, to, direction, listener) { + var step = direction * precision; + if (from != null) { + from = d3_geo_circleAngle(cr, from); + to = d3_geo_circleAngle(cr, to); + if (direction > 0 ? from < to : from > to) from += direction * τ; + } else { + from = radius + direction * τ; + to = radius - .5 * step; + } + for (var point, t = from; direction > 0 ? t > to : t < to; t -= step) { + listener.point((point = d3_geo_spherical([ cr, -sr * Math.cos(t), -sr * Math.sin(t) ]))[0], point[1]); + } + }; + } + function d3_geo_circleAngle(cr, point) { + var a = d3_geo_cartesian(point); + a[0] -= cr; + d3_geo_cartesianNormalize(a); + var angle = d3_acos(-a[1]); + return ((-a[2] < 0 ? -angle : angle) + 2 * Math.PI - ε) % (2 * Math.PI); + } + d3.geo.distance = function(a, b) { + var Δλ = (b[0] - a[0]) * d3_radians, φ0 = a[1] * d3_radians, φ1 = b[1] * d3_radians, sinΔλ = Math.sin(Δλ), cosΔλ = Math.cos(Δλ), sinφ0 = Math.sin(φ0), cosφ0 = Math.cos(φ0), sinφ1 = Math.sin(φ1), cosφ1 = Math.cos(φ1), t; + return Math.atan2(Math.sqrt((t = cosφ1 * sinΔλ) * t + (t = cosφ0 * sinφ1 - sinφ0 * cosφ1 * cosΔλ) * t), sinφ0 * sinφ1 + cosφ0 * cosφ1 * cosΔλ); + }; + d3.geo.graticule = function() { + var x1, x0, X1, X0, y1, y0, Y1, Y0, dx = 10, dy = dx, DX = 90, DY = 360, x, y, X, Y, precision = 2.5; + function graticule() { + return { + type: "MultiLineString", + coordinates: lines() + }; + } + function lines() { + return d3.range(Math.ceil(X0 / DX) * DX, X1, DX).map(X).concat(d3.range(Math.ceil(Y0 / DY) * DY, Y1, DY).map(Y)).concat(d3.range(Math.ceil(x0 / dx) * dx, x1, dx).filter(function(x) { + return abs(x % DX) > ε; + }).map(x)).concat(d3.range(Math.ceil(y0 / dy) * dy, y1, dy).filter(function(y) { + return abs(y % DY) > ε; + }).map(y)); + } + graticule.lines = function() { + return lines().map(function(coordinates) { + return { + type: "LineString", + coordinates: coordinates + }; + }); + }; + graticule.outline = function() { + return { + type: "Polygon", + coordinates: [ X(X0).concat(Y(Y1).slice(1), X(X1).reverse().slice(1), Y(Y0).reverse().slice(1)) ] + }; + }; + graticule.extent = function(_) { + if (!arguments.length) return graticule.minorExtent(); + return graticule.majorExtent(_).minorExtent(_); + }; + graticule.majorExtent = function(_) { + if (!arguments.length) return [ [ X0, Y0 ], [ X1, Y1 ] ]; + X0 = +_[0][0], X1 = +_[1][0]; + Y0 = +_[0][1], Y1 = +_[1][1]; + if (X0 > X1) _ = X0, X0 = X1, X1 = _; + if (Y0 > Y1) _ = Y0, Y0 = Y1, Y1 = _; + return graticule.precision(precision); + }; + graticule.minorExtent = function(_) { + if (!arguments.length) return [ [ x0, y0 ], [ x1, y1 ] ]; + x0 = +_[0][0], x1 = +_[1][0]; + y0 = +_[0][1], y1 = +_[1][1]; + if (x0 > x1) _ = x0, x0 = x1, x1 = _; + if (y0 > y1) _ = y0, y0 = y1, y1 = _; + return graticule.precision(precision); + }; + graticule.step = function(_) { + if (!arguments.length) return graticule.minorStep(); + return graticule.majorStep(_).minorStep(_); + }; + graticule.majorStep = function(_) { + if (!arguments.length) return [ DX, DY ]; + DX = +_[0], DY = +_[1]; + return graticule; + }; + graticule.minorStep = function(_) { + if (!arguments.length) return [ dx, dy ]; + dx = +_[0], dy = +_[1]; + return graticule; + }; + graticule.precision = function(_) { + if (!arguments.length) return precision; + precision = +_; + x = d3_geo_graticuleX(y0, y1, 90); + y = d3_geo_graticuleY(x0, x1, precision); + X = d3_geo_graticuleX(Y0, Y1, 90); + Y = d3_geo_graticuleY(X0, X1, precision); + return graticule; + }; + return graticule.majorExtent([ [ -180, -90 + ε ], [ 180, 90 - ε ] ]).minorExtent([ [ -180, -80 - ε ], [ 180, 80 + ε ] ]); + }; + function d3_geo_graticuleX(y0, y1, dy) { + var y = d3.range(y0, y1 - ε, dy).concat(y1); + return function(x) { + return y.map(function(y) { + return [ x, y ]; + }); + }; + } + function d3_geo_graticuleY(x0, x1, dx) { + var x = d3.range(x0, x1 - ε, dx).concat(x1); + return function(y) { + return x.map(function(x) { + return [ x, y ]; + }); + }; + } + function d3_source(d) { + return d.source; + } + function d3_target(d) { + return d.target; + } + d3.geo.greatArc = function() { + var source = d3_source, source_, target = d3_target, target_; + function greatArc() { + return { + type: "LineString", + coordinates: [ source_ || source.apply(this, arguments), target_ || target.apply(this, arguments) ] + }; + } + greatArc.distance = function() { + return d3.geo.distance(source_ || source.apply(this, arguments), target_ || target.apply(this, arguments)); + }; + greatArc.source = function(_) { + if (!arguments.length) return source; + source = _, source_ = typeof _ === "function" ? null : _; + return greatArc; + }; + greatArc.target = function(_) { + if (!arguments.length) return target; + target = _, target_ = typeof _ === "function" ? null : _; + return greatArc; + }; + greatArc.precision = function() { + return arguments.length ? greatArc : 0; + }; + return greatArc; + }; + d3.geo.interpolate = function(source, target) { + return d3_geo_interpolate(source[0] * d3_radians, source[1] * d3_radians, target[0] * d3_radians, target[1] * d3_radians); + }; + function d3_geo_interpolate(x0, y0, x1, y1) { + var cy0 = Math.cos(y0), sy0 = Math.sin(y0), cy1 = Math.cos(y1), sy1 = Math.sin(y1), kx0 = cy0 * Math.cos(x0), ky0 = cy0 * Math.sin(x0), kx1 = cy1 * Math.cos(x1), ky1 = cy1 * Math.sin(x1), d = 2 * Math.asin(Math.sqrt(d3_haversin(y1 - y0) + cy0 * cy1 * d3_haversin(x1 - x0))), k = 1 / Math.sin(d); + var interpolate = d ? function(t) { + var B = Math.sin(t *= d) * k, A = Math.sin(d - t) * k, x = A * kx0 + B * kx1, y = A * ky0 + B * ky1, z = A * sy0 + B * sy1; + return [ Math.atan2(y, x) * d3_degrees, Math.atan2(z, Math.sqrt(x * x + y * y)) * d3_degrees ]; + } : function() { + return [ x0 * d3_degrees, y0 * d3_degrees ]; + }; + interpolate.distance = d; + return interpolate; + } + d3.geo.length = function(object) { + d3_geo_lengthSum = 0; + d3.geo.stream(object, d3_geo_length); + return d3_geo_lengthSum; + }; + var d3_geo_lengthSum; + var d3_geo_length = { + sphere: d3_noop, + point: d3_noop, + lineStart: d3_geo_lengthLineStart, + lineEnd: d3_noop, + polygonStart: d3_noop, + polygonEnd: d3_noop + }; + function d3_geo_lengthLineStart() { + var λ0, sinφ0, cosφ0; + d3_geo_length.point = function(λ, φ) { + λ0 = λ * d3_radians, sinφ0 = Math.sin(φ *= d3_radians), cosφ0 = Math.cos(φ); + d3_geo_length.point = nextPoint; + }; + d3_geo_length.lineEnd = function() { + d3_geo_length.point = d3_geo_length.lineEnd = d3_noop; + }; + function nextPoint(λ, φ) { + var sinφ = Math.sin(φ *= d3_radians), cosφ = Math.cos(φ), t = abs((λ *= d3_radians) - λ0), cosΔλ = Math.cos(t); + d3_geo_lengthSum += Math.atan2(Math.sqrt((t = cosφ * Math.sin(t)) * t + (t = cosφ0 * sinφ - sinφ0 * cosφ * cosΔλ) * t), sinφ0 * sinφ + cosφ0 * cosφ * cosΔλ); + λ0 = λ, sinφ0 = sinφ, cosφ0 = cosφ; + } + } + function d3_geo_azimuthal(scale, angle) { + function azimuthal(λ, φ) { + var cosλ = Math.cos(λ), cosφ = Math.cos(φ), k = scale(cosλ * cosφ); + return [ k * cosφ * Math.sin(λ), k * Math.sin(φ) ]; + } + azimuthal.invert = function(x, y) { + var ρ = Math.sqrt(x * x + y * y), c = angle(ρ), sinc = Math.sin(c), cosc = Math.cos(c); + return [ Math.atan2(x * sinc, ρ * cosc), Math.asin(ρ && y * sinc / ρ) ]; + }; + return azimuthal; + } + var d3_geo_azimuthalEqualArea = d3_geo_azimuthal(function(cosλcosφ) { + return Math.sqrt(2 / (1 + cosλcosφ)); + }, function(ρ) { + return 2 * Math.asin(ρ / 2); + }); + (d3.geo.azimuthalEqualArea = function() { + return d3_geo_projection(d3_geo_azimuthalEqualArea); + }).raw = d3_geo_azimuthalEqualArea; + var d3_geo_azimuthalEquidistant = d3_geo_azimuthal(function(cosλcosφ) { + var c = Math.acos(cosλcosφ); + return c && c / Math.sin(c); + }, d3_identity); + (d3.geo.azimuthalEquidistant = function() { + return d3_geo_projection(d3_geo_azimuthalEquidistant); + }).raw = d3_geo_azimuthalEquidistant; + function d3_geo_conicConformal(φ0, φ1) { + var cosφ0 = Math.cos(φ0), t = function(φ) { + return Math.tan(π / 4 + φ / 2); + }, n = φ0 === φ1 ? Math.sin(φ0) : Math.log(cosφ0 / Math.cos(φ1)) / Math.log(t(φ1) / t(φ0)), F = cosφ0 * Math.pow(t(φ0), n) / n; + if (!n) return d3_geo_mercator; + function forward(λ, φ) { + if (F > 0) { + if (φ < -halfπ + ε) φ = -halfπ + ε; + } else { + if (φ > halfπ - ε) φ = halfπ - ε; + } + var ρ = F / Math.pow(t(φ), n); + return [ ρ * Math.sin(n * λ), F - ρ * Math.cos(n * λ) ]; + } + forward.invert = function(x, y) { + var ρ0_y = F - y, ρ = d3_sgn(n) * Math.sqrt(x * x + ρ0_y * ρ0_y); + return [ Math.atan2(x, ρ0_y) / n, 2 * Math.atan(Math.pow(F / ρ, 1 / n)) - halfπ ]; + }; + return forward; + } + (d3.geo.conicConformal = function() { + return d3_geo_conic(d3_geo_conicConformal); + }).raw = d3_geo_conicConformal; + function d3_geo_conicEquidistant(φ0, φ1) { + var cosφ0 = Math.cos(φ0), n = φ0 === φ1 ? Math.sin(φ0) : (cosφ0 - Math.cos(φ1)) / (φ1 - φ0), G = cosφ0 / n + φ0; + if (abs(n) < ε) return d3_geo_equirectangular; + function forward(λ, φ) { + var ρ = G - φ; + return [ ρ * Math.sin(n * λ), G - ρ * Math.cos(n * λ) ]; + } + forward.invert = function(x, y) { + var ρ0_y = G - y; + return [ Math.atan2(x, ρ0_y) / n, G - d3_sgn(n) * Math.sqrt(x * x + ρ0_y * ρ0_y) ]; + }; + return forward; + } + (d3.geo.conicEquidistant = function() { + return d3_geo_conic(d3_geo_conicEquidistant); + }).raw = d3_geo_conicEquidistant; + var d3_geo_gnomonic = d3_geo_azimuthal(function(cosλcosφ) { + return 1 / cosλcosφ; + }, Math.atan); + (d3.geo.gnomonic = function() { + return d3_geo_projection(d3_geo_gnomonic); + }).raw = d3_geo_gnomonic; + function d3_geo_mercator(λ, φ) { + return [ λ, Math.log(Math.tan(π / 4 + φ / 2)) ]; + } + d3_geo_mercator.invert = function(x, y) { + return [ x, 2 * Math.atan(Math.exp(y)) - halfπ ]; + }; + function d3_geo_mercatorProjection(project) { + var m = d3_geo_projection(project), scale = m.scale, translate = m.translate, clipExtent = m.clipExtent, clipAuto; + m.scale = function() { + var v = scale.apply(m, arguments); + return v === m ? clipAuto ? m.clipExtent(null) : m : v; + }; + m.translate = function() { + var v = translate.apply(m, arguments); + return v === m ? clipAuto ? m.clipExtent(null) : m : v; + }; + m.clipExtent = function(_) { + var v = clipExtent.apply(m, arguments); + if (v === m) { + if (clipAuto = _ == null) { + var k = π * scale(), t = translate(); + clipExtent([ [ t[0] - k, t[1] - k ], [ t[0] + k, t[1] + k ] ]); + } + } else if (clipAuto) { + v = null; + } + return v; + }; + return m.clipExtent(null); + } + (d3.geo.mercator = function() { + return d3_geo_mercatorProjection(d3_geo_mercator); + }).raw = d3_geo_mercator; + var d3_geo_orthographic = d3_geo_azimuthal(function() { + return 1; + }, Math.asin); + (d3.geo.orthographic = function() { + return d3_geo_projection(d3_geo_orthographic); + }).raw = d3_geo_orthographic; + var d3_geo_stereographic = d3_geo_azimuthal(function(cosλcosφ) { + return 1 / (1 + cosλcosφ); + }, function(ρ) { + return 2 * Math.atan(ρ); + }); + (d3.geo.stereographic = function() { + return d3_geo_projection(d3_geo_stereographic); + }).raw = d3_geo_stereographic; + function d3_geo_transverseMercator(λ, φ) { + return [ Math.log(Math.tan(π / 4 + φ / 2)), -λ ]; + } + d3_geo_transverseMercator.invert = function(x, y) { + return [ -y, 2 * Math.atan(Math.exp(x)) - halfπ ]; + }; + (d3.geo.transverseMercator = function() { + var projection = d3_geo_mercatorProjection(d3_geo_transverseMercator), center = projection.center, rotate = projection.rotate; + projection.center = function(_) { + return _ ? center([ -_[1], _[0] ]) : (_ = center(), [ -_[1], _[0] ]); + }; + projection.rotate = function(_) { + return _ ? rotate([ _[0], _[1], _.length > 2 ? _[2] + 90 : 90 ]) : (_ = rotate(), + [ _[0], _[1], _[2] - 90 ]); + }; + return projection.rotate([ 0, 0 ]); + }).raw = d3_geo_transverseMercator; + d3.geom = {}; + function d3_geom_pointX(d) { + return d[0]; + } + function d3_geom_pointY(d) { + return d[1]; + } + d3.geom.hull = function(vertices) { + var x = d3_geom_pointX, y = d3_geom_pointY; + if (arguments.length) return hull(vertices); + function hull(data) { + if (data.length < 3) return []; + var fx = d3_functor(x), fy = d3_functor(y), i, n = data.length, points = [], flippedPoints = []; + for (i = 0; i < n; i++) { + points.push([ +fx.call(this, data[i], i), +fy.call(this, data[i], i), i ]); + } + points.sort(d3_geom_hullOrder); + for (i = 0; i < n; i++) flippedPoints.push([ points[i][0], -points[i][1] ]); + var upper = d3_geom_hullUpper(points), lower = d3_geom_hullUpper(flippedPoints); + var skipLeft = lower[0] === upper[0], skipRight = lower[lower.length - 1] === upper[upper.length - 1], polygon = []; + for (i = upper.length - 1; i >= 0; --i) polygon.push(data[points[upper[i]][2]]); + for (i = +skipLeft; i < lower.length - skipRight; ++i) polygon.push(data[points[lower[i]][2]]); + return polygon; + } + hull.x = function(_) { + return arguments.length ? (x = _, hull) : x; + }; + hull.y = function(_) { + return arguments.length ? (y = _, hull) : y; + }; + return hull; + }; + function d3_geom_hullUpper(points) { + var n = points.length, hull = [ 0, 1 ], hs = 2; + for (var i = 2; i < n; i++) { + while (hs > 1 && d3_cross2d(points[hull[hs - 2]], points[hull[hs - 1]], points[i]) <= 0) --hs; + hull[hs++] = i; + } + return hull.slice(0, hs); + } + function d3_geom_hullOrder(a, b) { + return a[0] - b[0] || a[1] - b[1]; + } + d3.geom.polygon = function(coordinates) { + d3_subclass(coordinates, d3_geom_polygonPrototype); + return coordinates; + }; + var d3_geom_polygonPrototype = d3.geom.polygon.prototype = []; + d3_geom_polygonPrototype.area = function() { + var i = -1, n = this.length, a, b = this[n - 1], area = 0; + while (++i < n) { + a = b; + b = this[i]; + area += a[1] * b[0] - a[0] * b[1]; + } + return area * .5; + }; + d3_geom_polygonPrototype.centroid = function(k) { + var i = -1, n = this.length, x = 0, y = 0, a, b = this[n - 1], c; + if (!arguments.length) k = -1 / (6 * this.area()); + while (++i < n) { + a = b; + b = this[i]; + c = a[0] * b[1] - b[0] * a[1]; + x += (a[0] + b[0]) * c; + y += (a[1] + b[1]) * c; + } + return [ x * k, y * k ]; + }; + d3_geom_polygonPrototype.clip = function(subject) { + var input, closed = d3_geom_polygonClosed(subject), i = -1, n = this.length - d3_geom_polygonClosed(this), j, m, a = this[n - 1], b, c, d; + while (++i < n) { + input = subject.slice(); + subject.length = 0; + b = this[i]; + c = input[(m = input.length - closed) - 1]; + j = -1; + while (++j < m) { + d = input[j]; + if (d3_geom_polygonInside(d, a, b)) { + if (!d3_geom_polygonInside(c, a, b)) { + subject.push(d3_geom_polygonIntersect(c, d, a, b)); + } + subject.push(d); + } else if (d3_geom_polygonInside(c, a, b)) { + subject.push(d3_geom_polygonIntersect(c, d, a, b)); + } + c = d; + } + if (closed) subject.push(subject[0]); + a = b; + } + return subject; + }; + function d3_geom_polygonInside(p, a, b) { + return (b[0] - a[0]) * (p[1] - a[1]) < (b[1] - a[1]) * (p[0] - a[0]); + } + function d3_geom_polygonIntersect(c, d, a, b) { + var x1 = c[0], x3 = a[0], x21 = d[0] - x1, x43 = b[0] - x3, y1 = c[1], y3 = a[1], y21 = d[1] - y1, y43 = b[1] - y3, ua = (x43 * (y1 - y3) - y43 * (x1 - x3)) / (y43 * x21 - x43 * y21); + return [ x1 + ua * x21, y1 + ua * y21 ]; + } + function d3_geom_polygonClosed(coordinates) { + var a = coordinates[0], b = coordinates[coordinates.length - 1]; + return !(a[0] - b[0] || a[1] - b[1]); + } + var d3_geom_voronoiEdges, d3_geom_voronoiCells, d3_geom_voronoiBeaches, d3_geom_voronoiBeachPool = [], d3_geom_voronoiFirstCircle, d3_geom_voronoiCircles, d3_geom_voronoiCirclePool = []; + function d3_geom_voronoiBeach() { + d3_geom_voronoiRedBlackNode(this); + this.edge = this.site = this.circle = null; + } + function d3_geom_voronoiCreateBeach(site) { + var beach = d3_geom_voronoiBeachPool.pop() || new d3_geom_voronoiBeach(); + beach.site = site; + return beach; + } + function d3_geom_voronoiDetachBeach(beach) { + d3_geom_voronoiDetachCircle(beach); + d3_geom_voronoiBeaches.remove(beach); + d3_geom_voronoiBeachPool.push(beach); + d3_geom_voronoiRedBlackNode(beach); + } + function d3_geom_voronoiRemoveBeach(beach) { + var circle = beach.circle, x = circle.x, y = circle.cy, vertex = { + x: x, + y: y + }, previous = beach.P, next = beach.N, disappearing = [ beach ]; + d3_geom_voronoiDetachBeach(beach); + var lArc = previous; + while (lArc.circle && abs(x - lArc.circle.x) < ε && abs(y - lArc.circle.cy) < ε) { + previous = lArc.P; + disappearing.unshift(lArc); + d3_geom_voronoiDetachBeach(lArc); + lArc = previous; + } + disappearing.unshift(lArc); + d3_geom_voronoiDetachCircle(lArc); + var rArc = next; + while (rArc.circle && abs(x - rArc.circle.x) < ε && abs(y - rArc.circle.cy) < ε) { + next = rArc.N; + disappearing.push(rArc); + d3_geom_voronoiDetachBeach(rArc); + rArc = next; + } + disappearing.push(rArc); + d3_geom_voronoiDetachCircle(rArc); + var nArcs = disappearing.length, iArc; + for (iArc = 1; iArc < nArcs; ++iArc) { + rArc = disappearing[iArc]; + lArc = disappearing[iArc - 1]; + d3_geom_voronoiSetEdgeEnd(rArc.edge, lArc.site, rArc.site, vertex); + } + lArc = disappearing[0]; + rArc = disappearing[nArcs - 1]; + rArc.edge = d3_geom_voronoiCreateEdge(lArc.site, rArc.site, null, vertex); + d3_geom_voronoiAttachCircle(lArc); + d3_geom_voronoiAttachCircle(rArc); + } + function d3_geom_voronoiAddBeach(site) { + var x = site.x, directrix = site.y, lArc, rArc, dxl, dxr, node = d3_geom_voronoiBeaches._; + while (node) { + dxl = d3_geom_voronoiLeftBreakPoint(node, directrix) - x; + if (dxl > ε) node = node.L; else { + dxr = x - d3_geom_voronoiRightBreakPoint(node, directrix); + if (dxr > ε) { + if (!node.R) { + lArc = node; + break; + } + node = node.R; + } else { + if (dxl > -ε) { + lArc = node.P; + rArc = node; + } else if (dxr > -ε) { + lArc = node; + rArc = node.N; + } else { + lArc = rArc = node; + } + break; + } + } + } + var newArc = d3_geom_voronoiCreateBeach(site); + d3_geom_voronoiBeaches.insert(lArc, newArc); + if (!lArc && !rArc) return; + if (lArc === rArc) { + d3_geom_voronoiDetachCircle(lArc); + rArc = d3_geom_voronoiCreateBeach(lArc.site); + d3_geom_voronoiBeaches.insert(newArc, rArc); + newArc.edge = rArc.edge = d3_geom_voronoiCreateEdge(lArc.site, newArc.site); + d3_geom_voronoiAttachCircle(lArc); + d3_geom_voronoiAttachCircle(rArc); + return; + } + if (!rArc) { + newArc.edge = d3_geom_voronoiCreateEdge(lArc.site, newArc.site); + return; + } + d3_geom_voronoiDetachCircle(lArc); + d3_geom_voronoiDetachCircle(rArc); + var lSite = lArc.site, ax = lSite.x, ay = lSite.y, bx = site.x - ax, by = site.y - ay, rSite = rArc.site, cx = rSite.x - ax, cy = rSite.y - ay, d = 2 * (bx * cy - by * cx), hb = bx * bx + by * by, hc = cx * cx + cy * cy, vertex = { + x: (cy * hb - by * hc) / d + ax, + y: (bx * hc - cx * hb) / d + ay + }; + d3_geom_voronoiSetEdgeEnd(rArc.edge, lSite, rSite, vertex); + newArc.edge = d3_geom_voronoiCreateEdge(lSite, site, null, vertex); + rArc.edge = d3_geom_voronoiCreateEdge(site, rSite, null, vertex); + d3_geom_voronoiAttachCircle(lArc); + d3_geom_voronoiAttachCircle(rArc); + } + function d3_geom_voronoiLeftBreakPoint(arc, directrix) { + var site = arc.site, rfocx = site.x, rfocy = site.y, pby2 = rfocy - directrix; + if (!pby2) return rfocx; + var lArc = arc.P; + if (!lArc) return -Infinity; + site = lArc.site; + var lfocx = site.x, lfocy = site.y, plby2 = lfocy - directrix; + if (!plby2) return lfocx; + var hl = lfocx - rfocx, aby2 = 1 / pby2 - 1 / plby2, b = hl / plby2; + if (aby2) return (-b + Math.sqrt(b * b - 2 * aby2 * (hl * hl / (-2 * plby2) - lfocy + plby2 / 2 + rfocy - pby2 / 2))) / aby2 + rfocx; + return (rfocx + lfocx) / 2; + } + function d3_geom_voronoiRightBreakPoint(arc, directrix) { + var rArc = arc.N; + if (rArc) return d3_geom_voronoiLeftBreakPoint(rArc, directrix); + var site = arc.site; + return site.y === directrix ? site.x : Infinity; + } + function d3_geom_voronoiCell(site) { + this.site = site; + this.edges = []; + } + d3_geom_voronoiCell.prototype.prepare = function() { + var halfEdges = this.edges, iHalfEdge = halfEdges.length, edge; + while (iHalfEdge--) { + edge = halfEdges[iHalfEdge].edge; + if (!edge.b || !edge.a) halfEdges.splice(iHalfEdge, 1); + } + halfEdges.sort(d3_geom_voronoiHalfEdgeOrder); + return halfEdges.length; + }; + function d3_geom_voronoiCloseCells(extent) { + var x0 = extent[0][0], x1 = extent[1][0], y0 = extent[0][1], y1 = extent[1][1], x2, y2, x3, y3, cells = d3_geom_voronoiCells, iCell = cells.length, cell, iHalfEdge, halfEdges, nHalfEdges, start, end; + while (iCell--) { + cell = cells[iCell]; + if (!cell || !cell.prepare()) continue; + halfEdges = cell.edges; + nHalfEdges = halfEdges.length; + iHalfEdge = 0; + while (iHalfEdge < nHalfEdges) { + end = halfEdges[iHalfEdge].end(), x3 = end.x, y3 = end.y; + start = halfEdges[++iHalfEdge % nHalfEdges].start(), x2 = start.x, y2 = start.y; + if (abs(x3 - x2) > ε || abs(y3 - y2) > ε) { + halfEdges.splice(iHalfEdge, 0, new d3_geom_voronoiHalfEdge(d3_geom_voronoiCreateBorderEdge(cell.site, end, abs(x3 - x0) < ε && y1 - y3 > ε ? { + x: x0, + y: abs(x2 - x0) < ε ? y2 : y1 + } : abs(y3 - y1) < ε && x1 - x3 > ε ? { + x: abs(y2 - y1) < ε ? x2 : x1, + y: y1 + } : abs(x3 - x1) < ε && y3 - y0 > ε ? { + x: x1, + y: abs(x2 - x1) < ε ? y2 : y0 + } : abs(y3 - y0) < ε && x3 - x0 > ε ? { + x: abs(y2 - y0) < ε ? x2 : x0, + y: y0 + } : null), cell.site, null)); + ++nHalfEdges; + } + } + } + } + function d3_geom_voronoiHalfEdgeOrder(a, b) { + return b.angle - a.angle; + } + function d3_geom_voronoiCircle() { + d3_geom_voronoiRedBlackNode(this); + this.x = this.y = this.arc = this.site = this.cy = null; + } + function d3_geom_voronoiAttachCircle(arc) { + var lArc = arc.P, rArc = arc.N; + if (!lArc || !rArc) return; + var lSite = lArc.site, cSite = arc.site, rSite = rArc.site; + if (lSite === rSite) return; + var bx = cSite.x, by = cSite.y, ax = lSite.x - bx, ay = lSite.y - by, cx = rSite.x - bx, cy = rSite.y - by; + var d = 2 * (ax * cy - ay * cx); + if (d >= -ε2) return; + var ha = ax * ax + ay * ay, hc = cx * cx + cy * cy, x = (cy * ha - ay * hc) / d, y = (ax * hc - cx * ha) / d, cy = y + by; + var circle = d3_geom_voronoiCirclePool.pop() || new d3_geom_voronoiCircle(); + circle.arc = arc; + circle.site = cSite; + circle.x = x + bx; + circle.y = cy + Math.sqrt(x * x + y * y); + circle.cy = cy; + arc.circle = circle; + var before = null, node = d3_geom_voronoiCircles._; + while (node) { + if (circle.y < node.y || circle.y === node.y && circle.x <= node.x) { + if (node.L) node = node.L; else { + before = node.P; + break; + } + } else { + if (node.R) node = node.R; else { + before = node; + break; + } + } + } + d3_geom_voronoiCircles.insert(before, circle); + if (!before) d3_geom_voronoiFirstCircle = circle; + } + function d3_geom_voronoiDetachCircle(arc) { + var circle = arc.circle; + if (circle) { + if (!circle.P) d3_geom_voronoiFirstCircle = circle.N; + d3_geom_voronoiCircles.remove(circle); + d3_geom_voronoiCirclePool.push(circle); + d3_geom_voronoiRedBlackNode(circle); + arc.circle = null; + } + } + function d3_geom_voronoiClipEdges(extent) { + var edges = d3_geom_voronoiEdges, clip = d3_geom_clipLine(extent[0][0], extent[0][1], extent[1][0], extent[1][1]), i = edges.length, e; + while (i--) { + e = edges[i]; + if (!d3_geom_voronoiConnectEdge(e, extent) || !clip(e) || abs(e.a.x - e.b.x) < ε && abs(e.a.y - e.b.y) < ε) { + e.a = e.b = null; + edges.splice(i, 1); + } + } + } + function d3_geom_voronoiConnectEdge(edge, extent) { + var vb = edge.b; + if (vb) return true; + var va = edge.a, x0 = extent[0][0], x1 = extent[1][0], y0 = extent[0][1], y1 = extent[1][1], lSite = edge.l, rSite = edge.r, lx = lSite.x, ly = lSite.y, rx = rSite.x, ry = rSite.y, fx = (lx + rx) / 2, fy = (ly + ry) / 2, fm, fb; + if (ry === ly) { + if (fx < x0 || fx >= x1) return; + if (lx > rx) { + if (!va) va = { + x: fx, + y: y0 + }; else if (va.y >= y1) return; + vb = { + x: fx, + y: y1 + }; + } else { + if (!va) va = { + x: fx, + y: y1 + }; else if (va.y < y0) return; + vb = { + x: fx, + y: y0 + }; + } + } else { + fm = (lx - rx) / (ry - ly); + fb = fy - fm * fx; + if (fm < -1 || fm > 1) { + if (lx > rx) { + if (!va) va = { + x: (y0 - fb) / fm, + y: y0 + }; else if (va.y >= y1) return; + vb = { + x: (y1 - fb) / fm, + y: y1 + }; + } else { + if (!va) va = { + x: (y1 - fb) / fm, + y: y1 + }; else if (va.y < y0) return; + vb = { + x: (y0 - fb) / fm, + y: y0 + }; + } + } else { + if (ly < ry) { + if (!va) va = { + x: x0, + y: fm * x0 + fb + }; else if (va.x >= x1) return; + vb = { + x: x1, + y: fm * x1 + fb + }; + } else { + if (!va) va = { + x: x1, + y: fm * x1 + fb + }; else if (va.x < x0) return; + vb = { + x: x0, + y: fm * x0 + fb + }; + } + } + } + edge.a = va; + edge.b = vb; + return true; + } + function d3_geom_voronoiEdge(lSite, rSite) { + this.l = lSite; + this.r = rSite; + this.a = this.b = null; + } + function d3_geom_voronoiCreateEdge(lSite, rSite, va, vb) { + var edge = new d3_geom_voronoiEdge(lSite, rSite); + d3_geom_voronoiEdges.push(edge); + if (va) d3_geom_voronoiSetEdgeEnd(edge, lSite, rSite, va); + if (vb) d3_geom_voronoiSetEdgeEnd(edge, rSite, lSite, vb); + d3_geom_voronoiCells[lSite.i].edges.push(new d3_geom_voronoiHalfEdge(edge, lSite, rSite)); + d3_geom_voronoiCells[rSite.i].edges.push(new d3_geom_voronoiHalfEdge(edge, rSite, lSite)); + return edge; + } + function d3_geom_voronoiCreateBorderEdge(lSite, va, vb) { + var edge = new d3_geom_voronoiEdge(lSite, null); + edge.a = va; + edge.b = vb; + d3_geom_voronoiEdges.push(edge); + return edge; + } + function d3_geom_voronoiSetEdgeEnd(edge, lSite, rSite, vertex) { + if (!edge.a && !edge.b) { + edge.a = vertex; + edge.l = lSite; + edge.r = rSite; + } else if (edge.l === rSite) { + edge.b = vertex; + } else { + edge.a = vertex; + } + } + function d3_geom_voronoiHalfEdge(edge, lSite, rSite) { + var va = edge.a, vb = edge.b; + this.edge = edge; + this.site = lSite; + this.angle = rSite ? Math.atan2(rSite.y - lSite.y, rSite.x - lSite.x) : edge.l === lSite ? Math.atan2(vb.x - va.x, va.y - vb.y) : Math.atan2(va.x - vb.x, vb.y - va.y); + } + d3_geom_voronoiHalfEdge.prototype = { + start: function() { + return this.edge.l === this.site ? this.edge.a : this.edge.b; + }, + end: function() { + return this.edge.l === this.site ? this.edge.b : this.edge.a; + } + }; + function d3_geom_voronoiRedBlackTree() { + this._ = null; + } + function d3_geom_voronoiRedBlackNode(node) { + node.U = node.C = node.L = node.R = node.P = node.N = null; + } + d3_geom_voronoiRedBlackTree.prototype = { + insert: function(after, node) { + var parent, grandpa, uncle; + if (after) { + node.P = after; + node.N = after.N; + if (after.N) after.N.P = node; + after.N = node; + if (after.R) { + after = after.R; + while (after.L) after = after.L; + after.L = node; + } else { + after.R = node; + } + parent = after; + } else if (this._) { + after = d3_geom_voronoiRedBlackFirst(this._); + node.P = null; + node.N = after; + after.P = after.L = node; + parent = after; + } else { + node.P = node.N = null; + this._ = node; + parent = null; + } + node.L = node.R = null; + node.U = parent; + node.C = true; + after = node; + while (parent && parent.C) { + grandpa = parent.U; + if (parent === grandpa.L) { + uncle = grandpa.R; + if (uncle && uncle.C) { + parent.C = uncle.C = false; + grandpa.C = true; + after = grandpa; + } else { + if (after === parent.R) { + d3_geom_voronoiRedBlackRotateLeft(this, parent); + after = parent; + parent = after.U; + } + parent.C = false; + grandpa.C = true; + d3_geom_voronoiRedBlackRotateRight(this, grandpa); + } + } else { + uncle = grandpa.L; + if (uncle && uncle.C) { + parent.C = uncle.C = false; + grandpa.C = true; + after = grandpa; + } else { + if (after === parent.L) { + d3_geom_voronoiRedBlackRotateRight(this, parent); + after = parent; + parent = after.U; + } + parent.C = false; + grandpa.C = true; + d3_geom_voronoiRedBlackRotateLeft(this, grandpa); + } + } + parent = after.U; + } + this._.C = false; + }, + remove: function(node) { + if (node.N) node.N.P = node.P; + if (node.P) node.P.N = node.N; + node.N = node.P = null; + var parent = node.U, sibling, left = node.L, right = node.R, next, red; + if (!left) next = right; else if (!right) next = left; else next = d3_geom_voronoiRedBlackFirst(right); + if (parent) { + if (parent.L === node) parent.L = next; else parent.R = next; + } else { + this._ = next; + } + if (left && right) { + red = next.C; + next.C = node.C; + next.L = left; + left.U = next; + if (next !== right) { + parent = next.U; + next.U = node.U; + node = next.R; + parent.L = node; + next.R = right; + right.U = next; + } else { + next.U = parent; + parent = next; + node = next.R; + } + } else { + red = node.C; + node = next; + } + if (node) node.U = parent; + if (red) return; + if (node && node.C) { + node.C = false; + return; + } + do { + if (node === this._) break; + if (node === parent.L) { + sibling = parent.R; + if (sibling.C) { + sibling.C = false; + parent.C = true; + d3_geom_voronoiRedBlackRotateLeft(this, parent); + sibling = parent.R; + } + if (sibling.L && sibling.L.C || sibling.R && sibling.R.C) { + if (!sibling.R || !sibling.R.C) { + sibling.L.C = false; + sibling.C = true; + d3_geom_voronoiRedBlackRotateRight(this, sibling); + sibling = parent.R; + } + sibling.C = parent.C; + parent.C = sibling.R.C = false; + d3_geom_voronoiRedBlackRotateLeft(this, parent); + node = this._; + break; + } + } else { + sibling = parent.L; + if (sibling.C) { + sibling.C = false; + parent.C = true; + d3_geom_voronoiRedBlackRotateRight(this, parent); + sibling = parent.L; + } + if (sibling.L && sibling.L.C || sibling.R && sibling.R.C) { + if (!sibling.L || !sibling.L.C) { + sibling.R.C = false; + sibling.C = true; + d3_geom_voronoiRedBlackRotateLeft(this, sibling); + sibling = parent.L; + } + sibling.C = parent.C; + parent.C = sibling.L.C = false; + d3_geom_voronoiRedBlackRotateRight(this, parent); + node = this._; + break; + } + } + sibling.C = true; + node = parent; + parent = parent.U; + } while (!node.C); + if (node) node.C = false; + } + }; + function d3_geom_voronoiRedBlackRotateLeft(tree, node) { + var p = node, q = node.R, parent = p.U; + if (parent) { + if (parent.L === p) parent.L = q; else parent.R = q; + } else { + tree._ = q; + } + q.U = parent; + p.U = q; + p.R = q.L; + if (p.R) p.R.U = p; + q.L = p; + } + function d3_geom_voronoiRedBlackRotateRight(tree, node) { + var p = node, q = node.L, parent = p.U; + if (parent) { + if (parent.L === p) parent.L = q; else parent.R = q; + } else { + tree._ = q; + } + q.U = parent; + p.U = q; + p.L = q.R; + if (p.L) p.L.U = p; + q.R = p; + } + function d3_geom_voronoiRedBlackFirst(node) { + while (node.L) node = node.L; + return node; + } + function d3_geom_voronoi(sites, bbox) { + var site = sites.sort(d3_geom_voronoiVertexOrder).pop(), x0, y0, circle; + d3_geom_voronoiEdges = []; + d3_geom_voronoiCells = new Array(sites.length); + d3_geom_voronoiBeaches = new d3_geom_voronoiRedBlackTree(); + d3_geom_voronoiCircles = new d3_geom_voronoiRedBlackTree(); + while (true) { + circle = d3_geom_voronoiFirstCircle; + if (site && (!circle || site.y < circle.y || site.y === circle.y && site.x < circle.x)) { + if (site.x !== x0 || site.y !== y0) { + d3_geom_voronoiCells[site.i] = new d3_geom_voronoiCell(site); + d3_geom_voronoiAddBeach(site); + x0 = site.x, y0 = site.y; + } + site = sites.pop(); + } else if (circle) { + d3_geom_voronoiRemoveBeach(circle.arc); + } else { + break; + } + } + if (bbox) d3_geom_voronoiClipEdges(bbox), d3_geom_voronoiCloseCells(bbox); + var diagram = { + cells: d3_geom_voronoiCells, + edges: d3_geom_voronoiEdges + }; + d3_geom_voronoiBeaches = d3_geom_voronoiCircles = d3_geom_voronoiEdges = d3_geom_voronoiCells = null; + return diagram; + } + function d3_geom_voronoiVertexOrder(a, b) { + return b.y - a.y || b.x - a.x; + } + d3.geom.voronoi = function(points) { + var x = d3_geom_pointX, y = d3_geom_pointY, fx = x, fy = y, clipExtent = d3_geom_voronoiClipExtent; + if (points) return voronoi(points); + function voronoi(data) { + var polygons = new Array(data.length), x0 = clipExtent[0][0], y0 = clipExtent[0][1], x1 = clipExtent[1][0], y1 = clipExtent[1][1]; + d3_geom_voronoi(sites(data), clipExtent).cells.forEach(function(cell, i) { + var edges = cell.edges, site = cell.site, polygon = polygons[i] = edges.length ? edges.map(function(e) { + var s = e.start(); + return [ s.x, s.y ]; + }) : site.x >= x0 && site.x <= x1 && site.y >= y0 && site.y <= y1 ? [ [ x0, y1 ], [ x1, y1 ], [ x1, y0 ], [ x0, y0 ] ] : []; + polygon.point = data[i]; + }); + return polygons; + } + function sites(data) { + return data.map(function(d, i) { + return { + x: Math.round(fx(d, i) / ε) * ε, + y: Math.round(fy(d, i) / ε) * ε, + i: i + }; + }); + } + voronoi.links = function(data) { + return d3_geom_voronoi(sites(data)).edges.filter(function(edge) { + return edge.l && edge.r; + }).map(function(edge) { + return { + source: data[edge.l.i], + target: data[edge.r.i] + }; + }); + }; + voronoi.triangles = function(data) { + var triangles = []; + d3_geom_voronoi(sites(data)).cells.forEach(function(cell, i) { + var site = cell.site, edges = cell.edges.sort(d3_geom_voronoiHalfEdgeOrder), j = -1, m = edges.length, e0, s0, e1 = edges[m - 1].edge, s1 = e1.l === site ? e1.r : e1.l; + while (++j < m) { + e0 = e1; + s0 = s1; + e1 = edges[j].edge; + s1 = e1.l === site ? e1.r : e1.l; + if (i < s0.i && i < s1.i && d3_geom_voronoiTriangleArea(site, s0, s1) < 0) { + triangles.push([ data[i], data[s0.i], data[s1.i] ]); + } + } + }); + return triangles; + }; + voronoi.x = function(_) { + return arguments.length ? (fx = d3_functor(x = _), voronoi) : x; + }; + voronoi.y = function(_) { + return arguments.length ? (fy = d3_functor(y = _), voronoi) : y; + }; + voronoi.clipExtent = function(_) { + if (!arguments.length) return clipExtent === d3_geom_voronoiClipExtent ? null : clipExtent; + clipExtent = _ == null ? d3_geom_voronoiClipExtent : _; + return voronoi; + }; + voronoi.size = function(_) { + if (!arguments.length) return clipExtent === d3_geom_voronoiClipExtent ? null : clipExtent && clipExtent[1]; + return voronoi.clipExtent(_ && [ [ 0, 0 ], _ ]); + }; + return voronoi; + }; + var d3_geom_voronoiClipExtent = [ [ -1e6, -1e6 ], [ 1e6, 1e6 ] ]; + function d3_geom_voronoiTriangleArea(a, b, c) { + return (a.x - c.x) * (b.y - a.y) - (a.x - b.x) * (c.y - a.y); + } + d3.geom.delaunay = function(vertices) { + return d3.geom.voronoi().triangles(vertices); + }; + d3.geom.quadtree = function(points, x1, y1, x2, y2) { + var x = d3_geom_pointX, y = d3_geom_pointY, compat; + if (compat = arguments.length) { + x = d3_geom_quadtreeCompatX; + y = d3_geom_quadtreeCompatY; + if (compat === 3) { + y2 = y1; + x2 = x1; + y1 = x1 = 0; + } + return quadtree(points); + } + function quadtree(data) { + var d, fx = d3_functor(x), fy = d3_functor(y), xs, ys, i, n, x1_, y1_, x2_, y2_; + if (x1 != null) { + x1_ = x1, y1_ = y1, x2_ = x2, y2_ = y2; + } else { + x2_ = y2_ = -(x1_ = y1_ = Infinity); + xs = [], ys = []; + n = data.length; + if (compat) for (i = 0; i < n; ++i) { + d = data[i]; + if (d.x < x1_) x1_ = d.x; + if (d.y < y1_) y1_ = d.y; + if (d.x > x2_) x2_ = d.x; + if (d.y > y2_) y2_ = d.y; + xs.push(d.x); + ys.push(d.y); + } else for (i = 0; i < n; ++i) { + var x_ = +fx(d = data[i], i), y_ = +fy(d, i); + if (x_ < x1_) x1_ = x_; + if (y_ < y1_) y1_ = y_; + if (x_ > x2_) x2_ = x_; + if (y_ > y2_) y2_ = y_; + xs.push(x_); + ys.push(y_); + } + } + var dx = x2_ - x1_, dy = y2_ - y1_; + if (dx > dy) y2_ = y1_ + dx; else x2_ = x1_ + dy; + function insert(n, d, x, y, x1, y1, x2, y2) { + if (isNaN(x) || isNaN(y)) return; + if (n.leaf) { + var nx = n.x, ny = n.y; + if (nx != null) { + if (abs(nx - x) + abs(ny - y) < .01) { + insertChild(n, d, x, y, x1, y1, x2, y2); + } else { + var nPoint = n.point; + n.x = n.y = n.point = null; + insertChild(n, nPoint, nx, ny, x1, y1, x2, y2); + insertChild(n, d, x, y, x1, y1, x2, y2); + } + } else { + n.x = x, n.y = y, n.point = d; + } + } else { + insertChild(n, d, x, y, x1, y1, x2, y2); + } + } + function insertChild(n, d, x, y, x1, y1, x2, y2) { + var sx = (x1 + x2) * .5, sy = (y1 + y2) * .5, right = x >= sx, bottom = y >= sy, i = (bottom << 1) + right; + n.leaf = false; + n = n.nodes[i] || (n.nodes[i] = d3_geom_quadtreeNode()); + if (right) x1 = sx; else x2 = sx; + if (bottom) y1 = sy; else y2 = sy; + insert(n, d, x, y, x1, y1, x2, y2); + } + var root = d3_geom_quadtreeNode(); + root.add = function(d) { + insert(root, d, +fx(d, ++i), +fy(d, i), x1_, y1_, x2_, y2_); + }; + root.visit = function(f) { + d3_geom_quadtreeVisit(f, root, x1_, y1_, x2_, y2_); + }; + i = -1; + if (x1 == null) { + while (++i < n) { + insert(root, data[i], xs[i], ys[i], x1_, y1_, x2_, y2_); + } + --i; + } else data.forEach(root.add); + xs = ys = data = d = null; + return root; + } + quadtree.x = function(_) { + return arguments.length ? (x = _, quadtree) : x; + }; + quadtree.y = function(_) { + return arguments.length ? (y = _, quadtree) : y; + }; + quadtree.extent = function(_) { + if (!arguments.length) return x1 == null ? null : [ [ x1, y1 ], [ x2, y2 ] ]; + if (_ == null) x1 = y1 = x2 = y2 = null; else x1 = +_[0][0], y1 = +_[0][1], x2 = +_[1][0], + y2 = +_[1][1]; + return quadtree; + }; + quadtree.size = function(_) { + if (!arguments.length) return x1 == null ? null : [ x2 - x1, y2 - y1 ]; + if (_ == null) x1 = y1 = x2 = y2 = null; else x1 = y1 = 0, x2 = +_[0], y2 = +_[1]; + return quadtree; + }; + return quadtree; + }; + function d3_geom_quadtreeCompatX(d) { + return d.x; + } + function d3_geom_quadtreeCompatY(d) { + return d.y; + } + function d3_geom_quadtreeNode() { + return { + leaf: true, + nodes: [], + point: null, + x: null, + y: null + }; + } + function d3_geom_quadtreeVisit(f, node, x1, y1, x2, y2) { + if (!f(node, x1, y1, x2, y2)) { + var sx = (x1 + x2) * .5, sy = (y1 + y2) * .5, children = node.nodes; + if (children[0]) d3_geom_quadtreeVisit(f, children[0], x1, y1, sx, sy); + if (children[1]) d3_geom_quadtreeVisit(f, children[1], sx, y1, x2, sy); + if (children[2]) d3_geom_quadtreeVisit(f, children[2], x1, sy, sx, y2); + if (children[3]) d3_geom_quadtreeVisit(f, children[3], sx, sy, x2, y2); + } + } + d3.interpolateRgb = d3_interpolateRgb; + function d3_interpolateRgb(a, b) { + a = d3.rgb(a); + b = d3.rgb(b); + var ar = a.r, ag = a.g, ab = a.b, br = b.r - ar, bg = b.g - ag, bb = b.b - ab; + return function(t) { + return "#" + d3_rgb_hex(Math.round(ar + br * t)) + d3_rgb_hex(Math.round(ag + bg * t)) + d3_rgb_hex(Math.round(ab + bb * t)); + }; + } + d3.interpolateObject = d3_interpolateObject; + function d3_interpolateObject(a, b) { + var i = {}, c = {}, k; + for (k in a) { + if (k in b) { + i[k] = d3_interpolate(a[k], b[k]); + } else { + c[k] = a[k]; + } + } + for (k in b) { + if (!(k in a)) { + c[k] = b[k]; + } + } + return function(t) { + for (k in i) c[k] = i[k](t); + return c; + }; + } + d3.interpolateNumber = d3_interpolateNumber; + function d3_interpolateNumber(a, b) { + b -= a = +a; + return function(t) { + return a + b * t; + }; + } + d3.interpolateString = d3_interpolateString; + function d3_interpolateString(a, b) { + var bi = d3_interpolate_numberA.lastIndex = d3_interpolate_numberB.lastIndex = 0, am, bm, bs, i = -1, s = [], q = []; + a = a + "", b = b + ""; + while ((am = d3_interpolate_numberA.exec(a)) && (bm = d3_interpolate_numberB.exec(b))) { + if ((bs = bm.index) > bi) { + bs = b.substring(bi, bs); + if (s[i]) s[i] += bs; else s[++i] = bs; + } + if ((am = am[0]) === (bm = bm[0])) { + if (s[i]) s[i] += bm; else s[++i] = bm; + } else { + s[++i] = null; + q.push({ + i: i, + x: d3_interpolateNumber(am, bm) + }); + } + bi = d3_interpolate_numberB.lastIndex; + } + if (bi < b.length) { + bs = b.substring(bi); + if (s[i]) s[i] += bs; else s[++i] = bs; + } + return s.length < 2 ? q[0] ? (b = q[0].x, function(t) { + return b(t) + ""; + }) : function() { + return b; + } : (b = q.length, function(t) { + for (var i = 0, o; i < b; ++i) s[(o = q[i]).i] = o.x(t); + return s.join(""); + }); + } + var d3_interpolate_numberA = /[-+]?(?:\d+\.?\d*|\.?\d+)(?:[eE][-+]?\d+)?/g, d3_interpolate_numberB = new RegExp(d3_interpolate_numberA.source, "g"); + d3.interpolate = d3_interpolate; + function d3_interpolate(a, b) { + var i = d3.interpolators.length, f; + while (--i >= 0 && !(f = d3.interpolators[i](a, b))) ; + return f; + } + d3.interpolators = [ function(a, b) { + var t = typeof b; + return (t === "string" ? d3_rgb_names.has(b) || /^(#|rgb\(|hsl\()/.test(b) ? d3_interpolateRgb : d3_interpolateString : b instanceof d3_Color ? d3_interpolateRgb : Array.isArray(b) ? d3_interpolateArray : t === "object" && isNaN(b) ? d3_interpolateObject : d3_interpolateNumber)(a, b); + } ]; + d3.interpolateArray = d3_interpolateArray; + function d3_interpolateArray(a, b) { + var x = [], c = [], na = a.length, nb = b.length, n0 = Math.min(a.length, b.length), i; + for (i = 0; i < n0; ++i) x.push(d3_interpolate(a[i], b[i])); + for (;i < na; ++i) c[i] = a[i]; + for (;i < nb; ++i) c[i] = b[i]; + return function(t) { + for (i = 0; i < n0; ++i) c[i] = x[i](t); + return c; + }; + } + var d3_ease_default = function() { + return d3_identity; + }; + var d3_ease = d3.map({ + linear: d3_ease_default, + poly: d3_ease_poly, + quad: function() { + return d3_ease_quad; + }, + cubic: function() { + return d3_ease_cubic; + }, + sin: function() { + return d3_ease_sin; + }, + exp: function() { + return d3_ease_exp; + }, + circle: function() { + return d3_ease_circle; + }, + elastic: d3_ease_elastic, + back: d3_ease_back, + bounce: function() { + return d3_ease_bounce; + } + }); + var d3_ease_mode = d3.map({ + "in": d3_identity, + out: d3_ease_reverse, + "in-out": d3_ease_reflect, + "out-in": function(f) { + return d3_ease_reflect(d3_ease_reverse(f)); + } + }); + d3.ease = function(name) { + var i = name.indexOf("-"), t = i >= 0 ? name.substring(0, i) : name, m = i >= 0 ? name.substring(i + 1) : "in"; + t = d3_ease.get(t) || d3_ease_default; + m = d3_ease_mode.get(m) || d3_identity; + return d3_ease_clamp(m(t.apply(null, d3_arraySlice.call(arguments, 1)))); + }; + function d3_ease_clamp(f) { + return function(t) { + return t <= 0 ? 0 : t >= 1 ? 1 : f(t); + }; + } + function d3_ease_reverse(f) { + return function(t) { + return 1 - f(1 - t); + }; + } + function d3_ease_reflect(f) { + return function(t) { + return .5 * (t < .5 ? f(2 * t) : 2 - f(2 - 2 * t)); + }; + } + function d3_ease_quad(t) { + return t * t; + } + function d3_ease_cubic(t) { + return t * t * t; + } + function d3_ease_cubicInOut(t) { + if (t <= 0) return 0; + if (t >= 1) return 1; + var t2 = t * t, t3 = t2 * t; + return 4 * (t < .5 ? t3 : 3 * (t - t2) + t3 - .75); + } + function d3_ease_poly(e) { + return function(t) { + return Math.pow(t, e); + }; + } + function d3_ease_sin(t) { + return 1 - Math.cos(t * halfπ); + } + function d3_ease_exp(t) { + return Math.pow(2, 10 * (t - 1)); + } + function d3_ease_circle(t) { + return 1 - Math.sqrt(1 - t * t); + } + function d3_ease_elastic(a, p) { + var s; + if (arguments.length < 2) p = .45; + if (arguments.length) s = p / τ * Math.asin(1 / a); else a = 1, s = p / 4; + return function(t) { + return 1 + a * Math.pow(2, -10 * t) * Math.sin((t - s) * τ / p); + }; + } + function d3_ease_back(s) { + if (!s) s = 1.70158; + return function(t) { + return t * t * ((s + 1) * t - s); + }; + } + function d3_ease_bounce(t) { + return t < 1 / 2.75 ? 7.5625 * t * t : t < 2 / 2.75 ? 7.5625 * (t -= 1.5 / 2.75) * t + .75 : t < 2.5 / 2.75 ? 7.5625 * (t -= 2.25 / 2.75) * t + .9375 : 7.5625 * (t -= 2.625 / 2.75) * t + .984375; + } + d3.interpolateHcl = d3_interpolateHcl; + function d3_interpolateHcl(a, b) { + a = d3.hcl(a); + b = d3.hcl(b); + var ah = a.h, ac = a.c, al = a.l, bh = b.h - ah, bc = b.c - ac, bl = b.l - al; + if (isNaN(bc)) bc = 0, ac = isNaN(ac) ? b.c : ac; + if (isNaN(bh)) bh = 0, ah = isNaN(ah) ? b.h : ah; else if (bh > 180) bh -= 360; else if (bh < -180) bh += 360; + return function(t) { + return d3_hcl_lab(ah + bh * t, ac + bc * t, al + bl * t) + ""; + }; + } + d3.interpolateHsl = d3_interpolateHsl; + function d3_interpolateHsl(a, b) { + a = d3.hsl(a); + b = d3.hsl(b); + var ah = a.h, as = a.s, al = a.l, bh = b.h - ah, bs = b.s - as, bl = b.l - al; + if (isNaN(bs)) bs = 0, as = isNaN(as) ? b.s : as; + if (isNaN(bh)) bh = 0, ah = isNaN(ah) ? b.h : ah; else if (bh > 180) bh -= 360; else if (bh < -180) bh += 360; + return function(t) { + return d3_hsl_rgb(ah + bh * t, as + bs * t, al + bl * t) + ""; + }; + } + d3.interpolateLab = d3_interpolateLab; + function d3_interpolateLab(a, b) { + a = d3.lab(a); + b = d3.lab(b); + var al = a.l, aa = a.a, ab = a.b, bl = b.l - al, ba = b.a - aa, bb = b.b - ab; + return function(t) { + return d3_lab_rgb(al + bl * t, aa + ba * t, ab + bb * t) + ""; + }; + } + d3.interpolateRound = d3_interpolateRound; + function d3_interpolateRound(a, b) { + b -= a; + return function(t) { + return Math.round(a + b * t); + }; + } + d3.transform = function(string) { + var g = d3_document.createElementNS(d3.ns.prefix.svg, "g"); + return (d3.transform = function(string) { + if (string != null) { + g.setAttribute("transform", string); + var t = g.transform.baseVal.consolidate(); + } + return new d3_transform(t ? t.matrix : d3_transformIdentity); + })(string); + }; + function d3_transform(m) { + var r0 = [ m.a, m.b ], r1 = [ m.c, m.d ], kx = d3_transformNormalize(r0), kz = d3_transformDot(r0, r1), ky = d3_transformNormalize(d3_transformCombine(r1, r0, -kz)) || 0; + if (r0[0] * r1[1] < r1[0] * r0[1]) { + r0[0] *= -1; + r0[1] *= -1; + kx *= -1; + kz *= -1; + } + this.rotate = (kx ? Math.atan2(r0[1], r0[0]) : Math.atan2(-r1[0], r1[1])) * d3_degrees; + this.translate = [ m.e, m.f ]; + this.scale = [ kx, ky ]; + this.skew = ky ? Math.atan2(kz, ky) * d3_degrees : 0; + } + d3_transform.prototype.toString = function() { + return "translate(" + this.translate + ")rotate(" + this.rotate + ")skewX(" + this.skew + ")scale(" + this.scale + ")"; + }; + function d3_transformDot(a, b) { + return a[0] * b[0] + a[1] * b[1]; + } + function d3_transformNormalize(a) { + var k = Math.sqrt(d3_transformDot(a, a)); + if (k) { + a[0] /= k; + a[1] /= k; + } + return k; + } + function d3_transformCombine(a, b, k) { + a[0] += k * b[0]; + a[1] += k * b[1]; + return a; + } + var d3_transformIdentity = { + a: 1, + b: 0, + c: 0, + d: 1, + e: 0, + f: 0 + }; + d3.interpolateTransform = d3_interpolateTransform; + function d3_interpolateTransform(a, b) { + var s = [], q = [], n, A = d3.transform(a), B = d3.transform(b), ta = A.translate, tb = B.translate, ra = A.rotate, rb = B.rotate, wa = A.skew, wb = B.skew, ka = A.scale, kb = B.scale; + if (ta[0] != tb[0] || ta[1] != tb[1]) { + s.push("translate(", null, ",", null, ")"); + q.push({ + i: 1, + x: d3_interpolateNumber(ta[0], tb[0]) + }, { + i: 3, + x: d3_interpolateNumber(ta[1], tb[1]) + }); + } else if (tb[0] || tb[1]) { + s.push("translate(" + tb + ")"); + } else { + s.push(""); + } + if (ra != rb) { + if (ra - rb > 180) rb += 360; else if (rb - ra > 180) ra += 360; + q.push({ + i: s.push(s.pop() + "rotate(", null, ")") - 2, + x: d3_interpolateNumber(ra, rb) + }); + } else if (rb) { + s.push(s.pop() + "rotate(" + rb + ")"); + } + if (wa != wb) { + q.push({ + i: s.push(s.pop() + "skewX(", null, ")") - 2, + x: d3_interpolateNumber(wa, wb) + }); + } else if (wb) { + s.push(s.pop() + "skewX(" + wb + ")"); + } + if (ka[0] != kb[0] || ka[1] != kb[1]) { + n = s.push(s.pop() + "scale(", null, ",", null, ")"); + q.push({ + i: n - 4, + x: d3_interpolateNumber(ka[0], kb[0]) + }, { + i: n - 2, + x: d3_interpolateNumber(ka[1], kb[1]) + }); + } else if (kb[0] != 1 || kb[1] != 1) { + s.push(s.pop() + "scale(" + kb + ")"); + } + n = q.length; + return function(t) { + var i = -1, o; + while (++i < n) s[(o = q[i]).i] = o.x(t); + return s.join(""); + }; + } + function d3_uninterpolateNumber(a, b) { + b = b - (a = +a) ? 1 / (b - a) : 0; + return function(x) { + return (x - a) * b; + }; + } + function d3_uninterpolateClamp(a, b) { + b = b - (a = +a) ? 1 / (b - a) : 0; + return function(x) { + return Math.max(0, Math.min(1, (x - a) * b)); + }; + } + d3.layout = {}; + d3.layout.bundle = function() { + return function(links) { + var paths = [], i = -1, n = links.length; + while (++i < n) paths.push(d3_layout_bundlePath(links[i])); + return paths; + }; + }; + function d3_layout_bundlePath(link) { + var start = link.source, end = link.target, lca = d3_layout_bundleLeastCommonAncestor(start, end), points = [ start ]; + while (start !== lca) { + start = start.parent; + points.push(start); + } + var k = points.length; + while (end !== lca) { + points.splice(k, 0, end); + end = end.parent; + } + return points; + } + function d3_layout_bundleAncestors(node) { + var ancestors = [], parent = node.parent; + while (parent != null) { + ancestors.push(node); + node = parent; + parent = parent.parent; + } + ancestors.push(node); + return ancestors; + } + function d3_layout_bundleLeastCommonAncestor(a, b) { + if (a === b) return a; + var aNodes = d3_layout_bundleAncestors(a), bNodes = d3_layout_bundleAncestors(b), aNode = aNodes.pop(), bNode = bNodes.pop(), sharedNode = null; + while (aNode === bNode) { + sharedNode = aNode; + aNode = aNodes.pop(); + bNode = bNodes.pop(); + } + return sharedNode; + } + d3.layout.chord = function() { + var chord = {}, chords, groups, matrix, n, padding = 0, sortGroups, sortSubgroups, sortChords; + function relayout() { + var subgroups = {}, groupSums = [], groupIndex = d3.range(n), subgroupIndex = [], k, x, x0, i, j; + chords = []; + groups = []; + k = 0, i = -1; + while (++i < n) { + x = 0, j = -1; + while (++j < n) { + x += matrix[i][j]; + } + groupSums.push(x); + subgroupIndex.push(d3.range(n)); + k += x; + } + if (sortGroups) { + groupIndex.sort(function(a, b) { + return sortGroups(groupSums[a], groupSums[b]); + }); + } + if (sortSubgroups) { + subgroupIndex.forEach(function(d, i) { + d.sort(function(a, b) { + return sortSubgroups(matrix[i][a], matrix[i][b]); + }); + }); + } + k = (τ - padding * n) / k; + x = 0, i = -1; + while (++i < n) { + x0 = x, j = -1; + while (++j < n) { + var di = groupIndex[i], dj = subgroupIndex[di][j], v = matrix[di][dj], a0 = x, a1 = x += v * k; + subgroups[di + "-" + dj] = { + index: di, + subindex: dj, + startAngle: a0, + endAngle: a1, + value: v + }; + } + groups[di] = { + index: di, + startAngle: x0, + endAngle: x, + value: (x - x0) / k + }; + x += padding; + } + i = -1; + while (++i < n) { + j = i - 1; + while (++j < n) { + var source = subgroups[i + "-" + j], target = subgroups[j + "-" + i]; + if (source.value || target.value) { + chords.push(source.value < target.value ? { + source: target, + target: source + } : { + source: source, + target: target + }); + } + } + } + if (sortChords) resort(); + } + function resort() { + chords.sort(function(a, b) { + return sortChords((a.source.value + a.target.value) / 2, (b.source.value + b.target.value) / 2); + }); + } + chord.matrix = function(x) { + if (!arguments.length) return matrix; + n = (matrix = x) && matrix.length; + chords = groups = null; + return chord; + }; + chord.padding = function(x) { + if (!arguments.length) return padding; + padding = x; + chords = groups = null; + return chord; + }; + chord.sortGroups = function(x) { + if (!arguments.length) return sortGroups; + sortGroups = x; + chords = groups = null; + return chord; + }; + chord.sortSubgroups = function(x) { + if (!arguments.length) return sortSubgroups; + sortSubgroups = x; + chords = null; + return chord; + }; + chord.sortChords = function(x) { + if (!arguments.length) return sortChords; + sortChords = x; + if (chords) resort(); + return chord; + }; + chord.chords = function() { + if (!chords) relayout(); + return chords; + }; + chord.groups = function() { + if (!groups) relayout(); + return groups; + }; + return chord; + }; + d3.layout.force = function() { + var force = {}, event = d3.dispatch("start", "tick", "end"), size = [ 1, 1 ], drag, alpha, friction = .9, linkDistance = d3_layout_forceLinkDistance, linkStrength = d3_layout_forceLinkStrength, charge = -30, chargeDistance2 = d3_layout_forceChargeDistance2, gravity = .1, theta2 = .64, nodes = [], links = [], distances, strengths, charges; + function repulse(node) { + return function(quad, x1, _, x2) { + if (quad.point !== node) { + var dx = quad.cx - node.x, dy = quad.cy - node.y, dw = x2 - x1, dn = dx * dx + dy * dy; + if (dw * dw / theta2 < dn) { + if (dn < chargeDistance2) { + var k = quad.charge / dn; + node.px -= dx * k; + node.py -= dy * k; + } + return true; + } + if (quad.point && dn && dn < chargeDistance2) { + var k = quad.pointCharge / dn; + node.px -= dx * k; + node.py -= dy * k; + } + } + return !quad.charge; + }; + } + force.tick = function() { + if ((alpha *= .99) < .005) { + event.end({ + type: "end", + alpha: alpha = 0 + }); + return true; + } + var n = nodes.length, m = links.length, q, i, o, s, t, l, k, x, y; + for (i = 0; i < m; ++i) { + o = links[i]; + s = o.source; + t = o.target; + x = t.x - s.x; + y = t.y - s.y; + if (l = x * x + y * y) { + l = alpha * strengths[i] * ((l = Math.sqrt(l)) - distances[i]) / l; + x *= l; + y *= l; + t.x -= x * (k = s.weight / (t.weight + s.weight)); + t.y -= y * k; + s.x += x * (k = 1 - k); + s.y += y * k; + } + } + if (k = alpha * gravity) { + x = size[0] / 2; + y = size[1] / 2; + i = -1; + if (k) while (++i < n) { + o = nodes[i]; + o.x += (x - o.x) * k; + o.y += (y - o.y) * k; + } + } + if (charge) { + d3_layout_forceAccumulate(q = d3.geom.quadtree(nodes), alpha, charges); + i = -1; + while (++i < n) { + if (!(o = nodes[i]).fixed) { + q.visit(repulse(o)); + } + } + } + i = -1; + while (++i < n) { + o = nodes[i]; + if (o.fixed) { + o.x = o.px; + o.y = o.py; + } else { + o.x -= (o.px - (o.px = o.x)) * friction; + o.y -= (o.py - (o.py = o.y)) * friction; + } + } + event.tick({ + type: "tick", + alpha: alpha + }); + }; + force.nodes = function(x) { + if (!arguments.length) return nodes; + nodes = x; + return force; + }; + force.links = function(x) { + if (!arguments.length) return links; + links = x; + return force; + }; + force.size = function(x) { + if (!arguments.length) return size; + size = x; + return force; + }; + force.linkDistance = function(x) { + if (!arguments.length) return linkDistance; + linkDistance = typeof x === "function" ? x : +x; + return force; + }; + force.distance = force.linkDistance; + force.linkStrength = function(x) { + if (!arguments.length) return linkStrength; + linkStrength = typeof x === "function" ? x : +x; + return force; + }; + force.friction = function(x) { + if (!arguments.length) return friction; + friction = +x; + return force; + }; + force.charge = function(x) { + if (!arguments.length) return charge; + charge = typeof x === "function" ? x : +x; + return force; + }; + force.chargeDistance = function(x) { + if (!arguments.length) return Math.sqrt(chargeDistance2); + chargeDistance2 = x * x; + return force; + }; + force.gravity = function(x) { + if (!arguments.length) return gravity; + gravity = +x; + return force; + }; + force.theta = function(x) { + if (!arguments.length) return Math.sqrt(theta2); + theta2 = x * x; + return force; + }; + force.alpha = function(x) { + if (!arguments.length) return alpha; + x = +x; + if (alpha) { + if (x > 0) alpha = x; else alpha = 0; + } else if (x > 0) { + event.start({ + type: "start", + alpha: alpha = x + }); + d3.timer(force.tick); + } + return force; + }; + force.start = function() { + var i, n = nodes.length, m = links.length, w = size[0], h = size[1], neighbors, o; + for (i = 0; i < n; ++i) { + (o = nodes[i]).index = i; + o.weight = 0; + } + for (i = 0; i < m; ++i) { + o = links[i]; + if (typeof o.source == "number") o.source = nodes[o.source]; + if (typeof o.target == "number") o.target = nodes[o.target]; + ++o.source.weight; + ++o.target.weight; + } + for (i = 0; i < n; ++i) { + o = nodes[i]; + if (isNaN(o.x)) o.x = position("x", w); + if (isNaN(o.y)) o.y = position("y", h); + if (isNaN(o.px)) o.px = o.x; + if (isNaN(o.py)) o.py = o.y; + } + distances = []; + if (typeof linkDistance === "function") for (i = 0; i < m; ++i) distances[i] = +linkDistance.call(this, links[i], i); else for (i = 0; i < m; ++i) distances[i] = linkDistance; + strengths = []; + if (typeof linkStrength === "function") for (i = 0; i < m; ++i) strengths[i] = +linkStrength.call(this, links[i], i); else for (i = 0; i < m; ++i) strengths[i] = linkStrength; + charges = []; + if (typeof charge === "function") for (i = 0; i < n; ++i) charges[i] = +charge.call(this, nodes[i], i); else for (i = 0; i < n; ++i) charges[i] = charge; + function position(dimension, size) { + if (!neighbors) { + neighbors = new Array(n); + for (j = 0; j < n; ++j) { + neighbors[j] = []; + } + for (j = 0; j < m; ++j) { + var o = links[j]; + neighbors[o.source.index].push(o.target); + neighbors[o.target.index].push(o.source); + } + } + var candidates = neighbors[i], j = -1, m = candidates.length, x; + while (++j < m) if (!isNaN(x = candidates[j][dimension])) return x; + return Math.random() * size; + } + return force.resume(); + }; + force.resume = function() { + return force.alpha(.1); + }; + force.stop = function() { + return force.alpha(0); + }; + force.drag = function() { + if (!drag) drag = d3.behavior.drag().origin(d3_identity).on("dragstart.force", d3_layout_forceDragstart).on("drag.force", dragmove).on("dragend.force", d3_layout_forceDragend); + if (!arguments.length) return drag; + this.on("mouseover.force", d3_layout_forceMouseover).on("mouseout.force", d3_layout_forceMouseout).call(drag); + }; + function dragmove(d) { + d.px = d3.event.x, d.py = d3.event.y; + force.resume(); + } + return d3.rebind(force, event, "on"); + }; + function d3_layout_forceDragstart(d) { + d.fixed |= 2; + } + function d3_layout_forceDragend(d) { + d.fixed &= ~6; + } + function d3_layout_forceMouseover(d) { + d.fixed |= 4; + d.px = d.x, d.py = d.y; + } + function d3_layout_forceMouseout(d) { + d.fixed &= ~4; + } + function d3_layout_forceAccumulate(quad, alpha, charges) { + var cx = 0, cy = 0; + quad.charge = 0; + if (!quad.leaf) { + var nodes = quad.nodes, n = nodes.length, i = -1, c; + while (++i < n) { + c = nodes[i]; + if (c == null) continue; + d3_layout_forceAccumulate(c, alpha, charges); + quad.charge += c.charge; + cx += c.charge * c.cx; + cy += c.charge * c.cy; + } + } + if (quad.point) { + if (!quad.leaf) { + quad.point.x += Math.random() - .5; + quad.point.y += Math.random() - .5; + } + var k = alpha * charges[quad.point.index]; + quad.charge += quad.pointCharge = k; + cx += k * quad.point.x; + cy += k * quad.point.y; + } + quad.cx = cx / quad.charge; + quad.cy = cy / quad.charge; + } + var d3_layout_forceLinkDistance = 20, d3_layout_forceLinkStrength = 1, d3_layout_forceChargeDistance2 = Infinity; + d3.layout.hierarchy = function() { + var sort = d3_layout_hierarchySort, children = d3_layout_hierarchyChildren, value = d3_layout_hierarchyValue; + function hierarchy(root) { + var stack = [ root ], nodes = [], node; + root.depth = 0; + while ((node = stack.pop()) != null) { + nodes.push(node); + if ((childs = children.call(hierarchy, node, node.depth)) && (n = childs.length)) { + var n, childs, child; + while (--n >= 0) { + stack.push(child = childs[n]); + child.parent = node; + child.depth = node.depth + 1; + } + if (value) node.value = 0; + node.children = childs; + } else { + if (value) node.value = +value.call(hierarchy, node, node.depth) || 0; + delete node.children; + } + } + d3_layout_hierarchyVisitAfter(root, function(node) { + var childs, parent; + if (sort && (childs = node.children)) childs.sort(sort); + if (value && (parent = node.parent)) parent.value += node.value; + }); + return nodes; + } + hierarchy.sort = function(x) { + if (!arguments.length) return sort; + sort = x; + return hierarchy; + }; + hierarchy.children = function(x) { + if (!arguments.length) return children; + children = x; + return hierarchy; + }; + hierarchy.value = function(x) { + if (!arguments.length) return value; + value = x; + return hierarchy; + }; + hierarchy.revalue = function(root) { + if (value) { + d3_layout_hierarchyVisitBefore(root, function(node) { + if (node.children) node.value = 0; + }); + d3_layout_hierarchyVisitAfter(root, function(node) { + var parent; + if (!node.children) node.value = +value.call(hierarchy, node, node.depth) || 0; + if (parent = node.parent) parent.value += node.value; + }); + } + return root; + }; + return hierarchy; + }; + function d3_layout_hierarchyRebind(object, hierarchy) { + d3.rebind(object, hierarchy, "sort", "children", "value"); + object.nodes = object; + object.links = d3_layout_hierarchyLinks; + return object; + } + function d3_layout_hierarchyVisitBefore(node, callback) { + var nodes = [ node ]; + while ((node = nodes.pop()) != null) { + callback(node); + if ((children = node.children) && (n = children.length)) { + var n, children; + while (--n >= 0) nodes.push(children[n]); + } + } + } + function d3_layout_hierarchyVisitAfter(node, callback) { + var nodes = [ node ], nodes2 = []; + while ((node = nodes.pop()) != null) { + nodes2.push(node); + if ((children = node.children) && (n = children.length)) { + var i = -1, n, children; + while (++i < n) nodes.push(children[i]); + } + } + while ((node = nodes2.pop()) != null) { + callback(node); + } + } + function d3_layout_hierarchyChildren(d) { + return d.children; + } + function d3_layout_hierarchyValue(d) { + return d.value; + } + function d3_layout_hierarchySort(a, b) { + return b.value - a.value; + } + function d3_layout_hierarchyLinks(nodes) { + return d3.merge(nodes.map(function(parent) { + return (parent.children || []).map(function(child) { + return { + source: parent, + target: child + }; + }); + })); + } + d3.layout.partition = function() { + var hierarchy = d3.layout.hierarchy(), size = [ 1, 1 ]; + function position(node, x, dx, dy) { + var children = node.children; + node.x = x; + node.y = node.depth * dy; + node.dx = dx; + node.dy = dy; + if (children && (n = children.length)) { + var i = -1, n, c, d; + dx = node.value ? dx / node.value : 0; + while (++i < n) { + position(c = children[i], x, d = c.value * dx, dy); + x += d; + } + } + } + function depth(node) { + var children = node.children, d = 0; + if (children && (n = children.length)) { + var i = -1, n; + while (++i < n) d = Math.max(d, depth(children[i])); + } + return 1 + d; + } + function partition(d, i) { + var nodes = hierarchy.call(this, d, i); + position(nodes[0], 0, size[0], size[1] / depth(nodes[0])); + return nodes; + } + partition.size = function(x) { + if (!arguments.length) return size; + size = x; + return partition; + }; + return d3_layout_hierarchyRebind(partition, hierarchy); + }; + d3.layout.pie = function() { + var value = Number, sort = d3_layout_pieSortByValue, startAngle = 0, endAngle = τ; + function pie(data) { + var values = data.map(function(d, i) { + return +value.call(pie, d, i); + }); + var a = +(typeof startAngle === "function" ? startAngle.apply(this, arguments) : startAngle); + var k = ((typeof endAngle === "function" ? endAngle.apply(this, arguments) : endAngle) - a) / d3.sum(values); + var index = d3.range(data.length); + if (sort != null) index.sort(sort === d3_layout_pieSortByValue ? function(i, j) { + return values[j] - values[i]; + } : function(i, j) { + return sort(data[i], data[j]); + }); + var arcs = []; + index.forEach(function(i) { + var d; + arcs[i] = { + data: data[i], + value: d = values[i], + startAngle: a, + endAngle: a += d * k + }; + }); + return arcs; + } + pie.value = function(x) { + if (!arguments.length) return value; + value = x; + return pie; + }; + pie.sort = function(x) { + if (!arguments.length) return sort; + sort = x; + return pie; + }; + pie.startAngle = function(x) { + if (!arguments.length) return startAngle; + startAngle = x; + return pie; + }; + pie.endAngle = function(x) { + if (!arguments.length) return endAngle; + endAngle = x; + return pie; + }; + return pie; + }; + var d3_layout_pieSortByValue = {}; + d3.layout.stack = function() { + var values = d3_identity, order = d3_layout_stackOrderDefault, offset = d3_layout_stackOffsetZero, out = d3_layout_stackOut, x = d3_layout_stackX, y = d3_layout_stackY; + function stack(data, index) { + var series = data.map(function(d, i) { + return values.call(stack, d, i); + }); + var points = series.map(function(d) { + return d.map(function(v, i) { + return [ x.call(stack, v, i), y.call(stack, v, i) ]; + }); + }); + var orders = order.call(stack, points, index); + series = d3.permute(series, orders); + points = d3.permute(points, orders); + var offsets = offset.call(stack, points, index); + var n = series.length, m = series[0].length, i, j, o; + for (j = 0; j < m; ++j) { + out.call(stack, series[0][j], o = offsets[j], points[0][j][1]); + for (i = 1; i < n; ++i) { + out.call(stack, series[i][j], o += points[i - 1][j][1], points[i][j][1]); + } + } + return data; + } + stack.values = function(x) { + if (!arguments.length) return values; + values = x; + return stack; + }; + stack.order = function(x) { + if (!arguments.length) return order; + order = typeof x === "function" ? x : d3_layout_stackOrders.get(x) || d3_layout_stackOrderDefault; + return stack; + }; + stack.offset = function(x) { + if (!arguments.length) return offset; + offset = typeof x === "function" ? x : d3_layout_stackOffsets.get(x) || d3_layout_stackOffsetZero; + return stack; + }; + stack.x = function(z) { + if (!arguments.length) return x; + x = z; + return stack; + }; + stack.y = function(z) { + if (!arguments.length) return y; + y = z; + return stack; + }; + stack.out = function(z) { + if (!arguments.length) return out; + out = z; + return stack; + }; + return stack; + }; + function d3_layout_stackX(d) { + return d.x; + } + function d3_layout_stackY(d) { + return d.y; + } + function d3_layout_stackOut(d, y0, y) { + d.y0 = y0; + d.y = y; + } + var d3_layout_stackOrders = d3.map({ + "inside-out": function(data) { + var n = data.length, i, j, max = data.map(d3_layout_stackMaxIndex), sums = data.map(d3_layout_stackReduceSum), index = d3.range(n).sort(function(a, b) { + return max[a] - max[b]; + }), top = 0, bottom = 0, tops = [], bottoms = []; + for (i = 0; i < n; ++i) { + j = index[i]; + if (top < bottom) { + top += sums[j]; + tops.push(j); + } else { + bottom += sums[j]; + bottoms.push(j); + } + } + return bottoms.reverse().concat(tops); + }, + reverse: function(data) { + return d3.range(data.length).reverse(); + }, + "default": d3_layout_stackOrderDefault + }); + var d3_layout_stackOffsets = d3.map({ + silhouette: function(data) { + var n = data.length, m = data[0].length, sums = [], max = 0, i, j, o, y0 = []; + for (j = 0; j < m; ++j) { + for (i = 0, o = 0; i < n; i++) o += data[i][j][1]; + if (o > max) max = o; + sums.push(o); + } + for (j = 0; j < m; ++j) { + y0[j] = (max - sums[j]) / 2; + } + return y0; + }, + wiggle: function(data) { + var n = data.length, x = data[0], m = x.length, i, j, k, s1, s2, s3, dx, o, o0, y0 = []; + y0[0] = o = o0 = 0; + for (j = 1; j < m; ++j) { + for (i = 0, s1 = 0; i < n; ++i) s1 += data[i][j][1]; + for (i = 0, s2 = 0, dx = x[j][0] - x[j - 1][0]; i < n; ++i) { + for (k = 0, s3 = (data[i][j][1] - data[i][j - 1][1]) / (2 * dx); k < i; ++k) { + s3 += (data[k][j][1] - data[k][j - 1][1]) / dx; + } + s2 += s3 * data[i][j][1]; + } + y0[j] = o -= s1 ? s2 / s1 * dx : 0; + if (o < o0) o0 = o; + } + for (j = 0; j < m; ++j) y0[j] -= o0; + return y0; + }, + expand: function(data) { + var n = data.length, m = data[0].length, k = 1 / n, i, j, o, y0 = []; + for (j = 0; j < m; ++j) { + for (i = 0, o = 0; i < n; i++) o += data[i][j][1]; + if (o) for (i = 0; i < n; i++) data[i][j][1] /= o; else for (i = 0; i < n; i++) data[i][j][1] = k; + } + for (j = 0; j < m; ++j) y0[j] = 0; + return y0; + }, + zero: d3_layout_stackOffsetZero + }); + function d3_layout_stackOrderDefault(data) { + return d3.range(data.length); + } + function d3_layout_stackOffsetZero(data) { + var j = -1, m = data[0].length, y0 = []; + while (++j < m) y0[j] = 0; + return y0; + } + function d3_layout_stackMaxIndex(array) { + var i = 1, j = 0, v = array[0][1], k, n = array.length; + for (;i < n; ++i) { + if ((k = array[i][1]) > v) { + j = i; + v = k; + } + } + return j; + } + function d3_layout_stackReduceSum(d) { + return d.reduce(d3_layout_stackSum, 0); + } + function d3_layout_stackSum(p, d) { + return p + d[1]; + } + d3.layout.histogram = function() { + var frequency = true, valuer = Number, ranger = d3_layout_histogramRange, binner = d3_layout_histogramBinSturges; + function histogram(data, i) { + var bins = [], values = data.map(valuer, this), range = ranger.call(this, values, i), thresholds = binner.call(this, range, values, i), bin, i = -1, n = values.length, m = thresholds.length - 1, k = frequency ? 1 : 1 / n, x; + while (++i < m) { + bin = bins[i] = []; + bin.dx = thresholds[i + 1] - (bin.x = thresholds[i]); + bin.y = 0; + } + if (m > 0) { + i = -1; + while (++i < n) { + x = values[i]; + if (x >= range[0] && x <= range[1]) { + bin = bins[d3.bisect(thresholds, x, 1, m) - 1]; + bin.y += k; + bin.push(data[i]); + } + } + } + return bins; + } + histogram.value = function(x) { + if (!arguments.length) return valuer; + valuer = x; + return histogram; + }; + histogram.range = function(x) { + if (!arguments.length) return ranger; + ranger = d3_functor(x); + return histogram; + }; + histogram.bins = function(x) { + if (!arguments.length) return binner; + binner = typeof x === "number" ? function(range) { + return d3_layout_histogramBinFixed(range, x); + } : d3_functor(x); + return histogram; + }; + histogram.frequency = function(x) { + if (!arguments.length) return frequency; + frequency = !!x; + return histogram; + }; + return histogram; + }; + function d3_layout_histogramBinSturges(range, values) { + return d3_layout_histogramBinFixed(range, Math.ceil(Math.log(values.length) / Math.LN2 + 1)); + } + function d3_layout_histogramBinFixed(range, n) { + var x = -1, b = +range[0], m = (range[1] - b) / n, f = []; + while (++x <= n) f[x] = m * x + b; + return f; + } + function d3_layout_histogramRange(values) { + return [ d3.min(values), d3.max(values) ]; + } + d3.layout.pack = function() { + var hierarchy = d3.layout.hierarchy().sort(d3_layout_packSort), padding = 0, size = [ 1, 1 ], radius; + function pack(d, i) { + var nodes = hierarchy.call(this, d, i), root = nodes[0], w = size[0], h = size[1], r = radius == null ? Math.sqrt : typeof radius === "function" ? radius : function() { + return radius; + }; + root.x = root.y = 0; + d3_layout_hierarchyVisitAfter(root, function(d) { + d.r = +r(d.value); + }); + d3_layout_hierarchyVisitAfter(root, d3_layout_packSiblings); + if (padding) { + var dr = padding * (radius ? 1 : Math.max(2 * root.r / w, 2 * root.r / h)) / 2; + d3_layout_hierarchyVisitAfter(root, function(d) { + d.r += dr; + }); + d3_layout_hierarchyVisitAfter(root, d3_layout_packSiblings); + d3_layout_hierarchyVisitAfter(root, function(d) { + d.r -= dr; + }); + } + d3_layout_packTransform(root, w / 2, h / 2, radius ? 1 : 1 / Math.max(2 * root.r / w, 2 * root.r / h)); + return nodes; + } + pack.size = function(_) { + if (!arguments.length) return size; + size = _; + return pack; + }; + pack.radius = function(_) { + if (!arguments.length) return radius; + radius = _ == null || typeof _ === "function" ? _ : +_; + return pack; + }; + pack.padding = function(_) { + if (!arguments.length) return padding; + padding = +_; + return pack; + }; + return d3_layout_hierarchyRebind(pack, hierarchy); + }; + function d3_layout_packSort(a, b) { + return a.value - b.value; + } + function d3_layout_packInsert(a, b) { + var c = a._pack_next; + a._pack_next = b; + b._pack_prev = a; + b._pack_next = c; + c._pack_prev = b; + } + function d3_layout_packSplice(a, b) { + a._pack_next = b; + b._pack_prev = a; + } + function d3_layout_packIntersects(a, b) { + var dx = b.x - a.x, dy = b.y - a.y, dr = a.r + b.r; + return .999 * dr * dr > dx * dx + dy * dy; + } + function d3_layout_packSiblings(node) { + if (!(nodes = node.children) || !(n = nodes.length)) return; + var nodes, xMin = Infinity, xMax = -Infinity, yMin = Infinity, yMax = -Infinity, a, b, c, i, j, k, n; + function bound(node) { + xMin = Math.min(node.x - node.r, xMin); + xMax = Math.max(node.x + node.r, xMax); + yMin = Math.min(node.y - node.r, yMin); + yMax = Math.max(node.y + node.r, yMax); + } + nodes.forEach(d3_layout_packLink); + a = nodes[0]; + a.x = -a.r; + a.y = 0; + bound(a); + if (n > 1) { + b = nodes[1]; + b.x = b.r; + b.y = 0; + bound(b); + if (n > 2) { + c = nodes[2]; + d3_layout_packPlace(a, b, c); + bound(c); + d3_layout_packInsert(a, c); + a._pack_prev = c; + d3_layout_packInsert(c, b); + b = a._pack_next; + for (i = 3; i < n; i++) { + d3_layout_packPlace(a, b, c = nodes[i]); + var isect = 0, s1 = 1, s2 = 1; + for (j = b._pack_next; j !== b; j = j._pack_next, s1++) { + if (d3_layout_packIntersects(j, c)) { + isect = 1; + break; + } + } + if (isect == 1) { + for (k = a._pack_prev; k !== j._pack_prev; k = k._pack_prev, s2++) { + if (d3_layout_packIntersects(k, c)) { + break; + } + } + } + if (isect) { + if (s1 < s2 || s1 == s2 && b.r < a.r) d3_layout_packSplice(a, b = j); else d3_layout_packSplice(a = k, b); + i--; + } else { + d3_layout_packInsert(a, c); + b = c; + bound(c); + } + } + } + } + var cx = (xMin + xMax) / 2, cy = (yMin + yMax) / 2, cr = 0; + for (i = 0; i < n; i++) { + c = nodes[i]; + c.x -= cx; + c.y -= cy; + cr = Math.max(cr, c.r + Math.sqrt(c.x * c.x + c.y * c.y)); + } + node.r = cr; + nodes.forEach(d3_layout_packUnlink); + } + function d3_layout_packLink(node) { + node._pack_next = node._pack_prev = node; + } + function d3_layout_packUnlink(node) { + delete node._pack_next; + delete node._pack_prev; + } + function d3_layout_packTransform(node, x, y, k) { + var children = node.children; + node.x = x += k * node.x; + node.y = y += k * node.y; + node.r *= k; + if (children) { + var i = -1, n = children.length; + while (++i < n) d3_layout_packTransform(children[i], x, y, k); + } + } + function d3_layout_packPlace(a, b, c) { + var db = a.r + c.r, dx = b.x - a.x, dy = b.y - a.y; + if (db && (dx || dy)) { + var da = b.r + c.r, dc = dx * dx + dy * dy; + da *= da; + db *= db; + var x = .5 + (db - da) / (2 * dc), y = Math.sqrt(Math.max(0, 2 * da * (db + dc) - (db -= dc) * db - da * da)) / (2 * dc); + c.x = a.x + x * dx + y * dy; + c.y = a.y + x * dy - y * dx; + } else { + c.x = a.x + db; + c.y = a.y; + } + } + d3.layout.tree = function() { + var hierarchy = d3.layout.hierarchy().sort(null).value(null), separation = d3_layout_treeSeparation, size = [ 1, 1 ], nodeSize = null; + function tree(d, i) { + var nodes = hierarchy.call(this, d, i), root0 = nodes[0], root1 = wrapTree(root0); + d3_layout_hierarchyVisitAfter(root1, firstWalk), root1.parent.m = -root1.z; + d3_layout_hierarchyVisitBefore(root1, secondWalk); + if (nodeSize) d3_layout_hierarchyVisitBefore(root0, sizeNode); else { + var left = root0, right = root0, bottom = root0; + d3_layout_hierarchyVisitBefore(root0, function(node) { + if (node.x < left.x) left = node; + if (node.x > right.x) right = node; + if (node.depth > bottom.depth) bottom = node; + }); + var tx = separation(left, right) / 2 - left.x, kx = size[0] / (right.x + separation(right, left) / 2 + tx), ky = size[1] / (bottom.depth || 1); + d3_layout_hierarchyVisitBefore(root0, function(node) { + node.x = (node.x + tx) * kx; + node.y = node.depth * ky; + }); + } + return nodes; + } + function wrapTree(root0) { + var root1 = { + A: null, + children: [ root0 ] + }, queue = [ root1 ], node1; + while ((node1 = queue.pop()) != null) { + for (var children = node1.children, child, i = 0, n = children.length; i < n; ++i) { + queue.push((children[i] = child = { + _: children[i], + parent: node1, + children: (child = children[i].children) && child.slice() || [], + A: null, + a: null, + z: 0, + m: 0, + c: 0, + s: 0, + t: null, + i: i + }).a = child); + } + } + return root1.children[0]; + } + function firstWalk(v) { + var children = v.children, siblings = v.parent.children, w = v.i ? siblings[v.i - 1] : null; + if (children.length) { + d3_layout_treeShift(v); + var midpoint = (children[0].z + children[children.length - 1].z) / 2; + if (w) { + v.z = w.z + separation(v._, w._); + v.m = v.z - midpoint; + } else { + v.z = midpoint; + } + } else if (w) { + v.z = w.z + separation(v._, w._); + } + v.parent.A = apportion(v, w, v.parent.A || siblings[0]); + } + function secondWalk(v) { + v._.x = v.z + v.parent.m; + v.m += v.parent.m; + } + function apportion(v, w, ancestor) { + if (w) { + var vip = v, vop = v, vim = w, vom = vip.parent.children[0], sip = vip.m, sop = vop.m, sim = vim.m, som = vom.m, shift; + while (vim = d3_layout_treeRight(vim), vip = d3_layout_treeLeft(vip), vim && vip) { + vom = d3_layout_treeLeft(vom); + vop = d3_layout_treeRight(vop); + vop.a = v; + shift = vim.z + sim - vip.z - sip + separation(vim._, vip._); + if (shift > 0) { + d3_layout_treeMove(d3_layout_treeAncestor(vim, v, ancestor), v, shift); + sip += shift; + sop += shift; + } + sim += vim.m; + sip += vip.m; + som += vom.m; + sop += vop.m; + } + if (vim && !d3_layout_treeRight(vop)) { + vop.t = vim; + vop.m += sim - sop; + } + if (vip && !d3_layout_treeLeft(vom)) { + vom.t = vip; + vom.m += sip - som; + ancestor = v; + } + } + return ancestor; + } + function sizeNode(node) { + node.x *= size[0]; + node.y = node.depth * size[1]; + } + tree.separation = function(x) { + if (!arguments.length) return separation; + separation = x; + return tree; + }; + tree.size = function(x) { + if (!arguments.length) return nodeSize ? null : size; + nodeSize = (size = x) == null ? sizeNode : null; + return tree; + }; + tree.nodeSize = function(x) { + if (!arguments.length) return nodeSize ? size : null; + nodeSize = (size = x) == null ? null : sizeNode; + return tree; + }; + return d3_layout_hierarchyRebind(tree, hierarchy); + }; + function d3_layout_treeSeparation(a, b) { + return a.parent == b.parent ? 1 : 2; + } + function d3_layout_treeLeft(v) { + var children = v.children; + return children.length ? children[0] : v.t; + } + function d3_layout_treeRight(v) { + var children = v.children, n; + return (n = children.length) ? children[n - 1] : v.t; + } + function d3_layout_treeMove(wm, wp, shift) { + var change = shift / (wp.i - wm.i); + wp.c -= change; + wp.s += shift; + wm.c += change; + wp.z += shift; + wp.m += shift; + } + function d3_layout_treeShift(v) { + var shift = 0, change = 0, children = v.children, i = children.length, w; + while (--i >= 0) { + w = children[i]; + w.z += shift; + w.m += shift; + shift += w.s + (change += w.c); + } + } + function d3_layout_treeAncestor(vim, v, ancestor) { + return vim.a.parent === v.parent ? vim.a : ancestor; + } + d3.layout.cluster = function() { + var hierarchy = d3.layout.hierarchy().sort(null).value(null), separation = d3_layout_treeSeparation, size = [ 1, 1 ], nodeSize = false; + function cluster(d, i) { + var nodes = hierarchy.call(this, d, i), root = nodes[0], previousNode, x = 0; + d3_layout_hierarchyVisitAfter(root, function(node) { + var children = node.children; + if (children && children.length) { + node.x = d3_layout_clusterX(children); + node.y = d3_layout_clusterY(children); + } else { + node.x = previousNode ? x += separation(node, previousNode) : 0; + node.y = 0; + previousNode = node; + } + }); + var left = d3_layout_clusterLeft(root), right = d3_layout_clusterRight(root), x0 = left.x - separation(left, right) / 2, x1 = right.x + separation(right, left) / 2; + d3_layout_hierarchyVisitAfter(root, nodeSize ? function(node) { + node.x = (node.x - root.x) * size[0]; + node.y = (root.y - node.y) * size[1]; + } : function(node) { + node.x = (node.x - x0) / (x1 - x0) * size[0]; + node.y = (1 - (root.y ? node.y / root.y : 1)) * size[1]; + }); + return nodes; + } + cluster.separation = function(x) { + if (!arguments.length) return separation; + separation = x; + return cluster; + }; + cluster.size = function(x) { + if (!arguments.length) return nodeSize ? null : size; + nodeSize = (size = x) == null; + return cluster; + }; + cluster.nodeSize = function(x) { + if (!arguments.length) return nodeSize ? size : null; + nodeSize = (size = x) != null; + return cluster; + }; + return d3_layout_hierarchyRebind(cluster, hierarchy); + }; + function d3_layout_clusterY(children) { + return 1 + d3.max(children, function(child) { + return child.y; + }); + } + function d3_layout_clusterX(children) { + return children.reduce(function(x, child) { + return x + child.x; + }, 0) / children.length; + } + function d3_layout_clusterLeft(node) { + var children = node.children; + return children && children.length ? d3_layout_clusterLeft(children[0]) : node; + } + function d3_layout_clusterRight(node) { + var children = node.children, n; + return children && (n = children.length) ? d3_layout_clusterRight(children[n - 1]) : node; + } + d3.layout.treemap = function() { + var hierarchy = d3.layout.hierarchy(), round = Math.round, size = [ 1, 1 ], padding = null, pad = d3_layout_treemapPadNull, sticky = false, stickies, mode = "squarify", ratio = .5 * (1 + Math.sqrt(5)); + function scale(children, k) { + var i = -1, n = children.length, child, area; + while (++i < n) { + area = (child = children[i]).value * (k < 0 ? 0 : k); + child.area = isNaN(area) || area <= 0 ? 0 : area; + } + } + function squarify(node) { + var children = node.children; + if (children && children.length) { + var rect = pad(node), row = [], remaining = children.slice(), child, best = Infinity, score, u = mode === "slice" ? rect.dx : mode === "dice" ? rect.dy : mode === "slice-dice" ? node.depth & 1 ? rect.dy : rect.dx : Math.min(rect.dx, rect.dy), n; + scale(remaining, rect.dx * rect.dy / node.value); + row.area = 0; + while ((n = remaining.length) > 0) { + row.push(child = remaining[n - 1]); + row.area += child.area; + if (mode !== "squarify" || (score = worst(row, u)) <= best) { + remaining.pop(); + best = score; + } else { + row.area -= row.pop().area; + position(row, u, rect, false); + u = Math.min(rect.dx, rect.dy); + row.length = row.area = 0; + best = Infinity; + } + } + if (row.length) { + position(row, u, rect, true); + row.length = row.area = 0; + } + children.forEach(squarify); + } + } + function stickify(node) { + var children = node.children; + if (children && children.length) { + var rect = pad(node), remaining = children.slice(), child, row = []; + scale(remaining, rect.dx * rect.dy / node.value); + row.area = 0; + while (child = remaining.pop()) { + row.push(child); + row.area += child.area; + if (child.z != null) { + position(row, child.z ? rect.dx : rect.dy, rect, !remaining.length); + row.length = row.area = 0; + } + } + children.forEach(stickify); + } + } + function worst(row, u) { + var s = row.area, r, rmax = 0, rmin = Infinity, i = -1, n = row.length; + while (++i < n) { + if (!(r = row[i].area)) continue; + if (r < rmin) rmin = r; + if (r > rmax) rmax = r; + } + s *= s; + u *= u; + return s ? Math.max(u * rmax * ratio / s, s / (u * rmin * ratio)) : Infinity; + } + function position(row, u, rect, flush) { + var i = -1, n = row.length, x = rect.x, y = rect.y, v = u ? round(row.area / u) : 0, o; + if (u == rect.dx) { + if (flush || v > rect.dy) v = rect.dy; + while (++i < n) { + o = row[i]; + o.x = x; + o.y = y; + o.dy = v; + x += o.dx = Math.min(rect.x + rect.dx - x, v ? round(o.area / v) : 0); + } + o.z = true; + o.dx += rect.x + rect.dx - x; + rect.y += v; + rect.dy -= v; + } else { + if (flush || v > rect.dx) v = rect.dx; + while (++i < n) { + o = row[i]; + o.x = x; + o.y = y; + o.dx = v; + y += o.dy = Math.min(rect.y + rect.dy - y, v ? round(o.area / v) : 0); + } + o.z = false; + o.dy += rect.y + rect.dy - y; + rect.x += v; + rect.dx -= v; + } + } + function treemap(d) { + var nodes = stickies || hierarchy(d), root = nodes[0]; + root.x = 0; + root.y = 0; + root.dx = size[0]; + root.dy = size[1]; + if (stickies) hierarchy.revalue(root); + scale([ root ], root.dx * root.dy / root.value); + (stickies ? stickify : squarify)(root); + if (sticky) stickies = nodes; + return nodes; + } + treemap.size = function(x) { + if (!arguments.length) return size; + size = x; + return treemap; + }; + treemap.padding = function(x) { + if (!arguments.length) return padding; + function padFunction(node) { + var p = x.call(treemap, node, node.depth); + return p == null ? d3_layout_treemapPadNull(node) : d3_layout_treemapPad(node, typeof p === "number" ? [ p, p, p, p ] : p); + } + function padConstant(node) { + return d3_layout_treemapPad(node, x); + } + var type; + pad = (padding = x) == null ? d3_layout_treemapPadNull : (type = typeof x) === "function" ? padFunction : type === "number" ? (x = [ x, x, x, x ], + padConstant) : padConstant; + return treemap; + }; + treemap.round = function(x) { + if (!arguments.length) return round != Number; + round = x ? Math.round : Number; + return treemap; + }; + treemap.sticky = function(x) { + if (!arguments.length) return sticky; + sticky = x; + stickies = null; + return treemap; + }; + treemap.ratio = function(x) { + if (!arguments.length) return ratio; + ratio = x; + return treemap; + }; + treemap.mode = function(x) { + if (!arguments.length) return mode; + mode = x + ""; + return treemap; + }; + return d3_layout_hierarchyRebind(treemap, hierarchy); + }; + function d3_layout_treemapPadNull(node) { + return { + x: node.x, + y: node.y, + dx: node.dx, + dy: node.dy + }; + } + function d3_layout_treemapPad(node, padding) { + var x = node.x + padding[3], y = node.y + padding[0], dx = node.dx - padding[1] - padding[3], dy = node.dy - padding[0] - padding[2]; + if (dx < 0) { + x += dx / 2; + dx = 0; + } + if (dy < 0) { + y += dy / 2; + dy = 0; + } + return { + x: x, + y: y, + dx: dx, + dy: dy + }; + } + d3.random = { + normal: function(µ, σ) { + var n = arguments.length; + if (n < 2) σ = 1; + if (n < 1) µ = 0; + return function() { + var x, y, r; + do { + x = Math.random() * 2 - 1; + y = Math.random() * 2 - 1; + r = x * x + y * y; + } while (!r || r > 1); + return µ + σ * x * Math.sqrt(-2 * Math.log(r) / r); + }; + }, + logNormal: function() { + var random = d3.random.normal.apply(d3, arguments); + return function() { + return Math.exp(random()); + }; + }, + bates: function(m) { + var random = d3.random.irwinHall(m); + return function() { + return random() / m; + }; + }, + irwinHall: function(m) { + return function() { + for (var s = 0, j = 0; j < m; j++) s += Math.random(); + return s; + }; + } + }; + d3.scale = {}; + function d3_scaleExtent(domain) { + var start = domain[0], stop = domain[domain.length - 1]; + return start < stop ? [ start, stop ] : [ stop, start ]; + } + function d3_scaleRange(scale) { + return scale.rangeExtent ? scale.rangeExtent() : d3_scaleExtent(scale.range()); + } + function d3_scale_bilinear(domain, range, uninterpolate, interpolate) { + var u = uninterpolate(domain[0], domain[1]), i = interpolate(range[0], range[1]); + return function(x) { + return i(u(x)); + }; + } + function d3_scale_nice(domain, nice) { + var i0 = 0, i1 = domain.length - 1, x0 = domain[i0], x1 = domain[i1], dx; + if (x1 < x0) { + dx = i0, i0 = i1, i1 = dx; + dx = x0, x0 = x1, x1 = dx; + } + domain[i0] = nice.floor(x0); + domain[i1] = nice.ceil(x1); + return domain; + } + function d3_scale_niceStep(step) { + return step ? { + floor: function(x) { + return Math.floor(x / step) * step; + }, + ceil: function(x) { + return Math.ceil(x / step) * step; + } + } : d3_scale_niceIdentity; + } + var d3_scale_niceIdentity = { + floor: d3_identity, + ceil: d3_identity + }; + function d3_scale_polylinear(domain, range, uninterpolate, interpolate) { + var u = [], i = [], j = 0, k = Math.min(domain.length, range.length) - 1; + if (domain[k] < domain[0]) { + domain = domain.slice().reverse(); + range = range.slice().reverse(); + } + while (++j <= k) { + u.push(uninterpolate(domain[j - 1], domain[j])); + i.push(interpolate(range[j - 1], range[j])); + } + return function(x) { + var j = d3.bisect(domain, x, 1, k) - 1; + return i[j](u[j](x)); + }; + } + d3.scale.linear = function() { + return d3_scale_linear([ 0, 1 ], [ 0, 1 ], d3_interpolate, false); + }; + function d3_scale_linear(domain, range, interpolate, clamp) { + var output, input; + function rescale() { + var linear = Math.min(domain.length, range.length) > 2 ? d3_scale_polylinear : d3_scale_bilinear, uninterpolate = clamp ? d3_uninterpolateClamp : d3_uninterpolateNumber; + output = linear(domain, range, uninterpolate, interpolate); + input = linear(range, domain, uninterpolate, d3_interpolate); + return scale; + } + function scale(x) { + return output(x); + } + scale.invert = function(y) { + return input(y); + }; + scale.domain = function(x) { + if (!arguments.length) return domain; + domain = x.map(Number); + return rescale(); + }; + scale.range = function(x) { + if (!arguments.length) return range; + range = x; + return rescale(); + }; + scale.rangeRound = function(x) { + return scale.range(x).interpolate(d3_interpolateRound); + }; + scale.clamp = function(x) { + if (!arguments.length) return clamp; + clamp = x; + return rescale(); + }; + scale.interpolate = function(x) { + if (!arguments.length) return interpolate; + interpolate = x; + return rescale(); + }; + scale.ticks = function(m) { + return d3_scale_linearTicks(domain, m); + }; + scale.tickFormat = function(m, format) { + return d3_scale_linearTickFormat(domain, m, format); + }; + scale.nice = function(m) { + d3_scale_linearNice(domain, m); + return rescale(); + }; + scale.copy = function() { + return d3_scale_linear(domain, range, interpolate, clamp); + }; + return rescale(); + } + function d3_scale_linearRebind(scale, linear) { + return d3.rebind(scale, linear, "range", "rangeRound", "interpolate", "clamp"); + } + function d3_scale_linearNice(domain, m) { + return d3_scale_nice(domain, d3_scale_niceStep(d3_scale_linearTickRange(domain, m)[2])); + } + function d3_scale_linearTickRange(domain, m) { + if (m == null) m = 10; + var extent = d3_scaleExtent(domain), span = extent[1] - extent[0], step = Math.pow(10, Math.floor(Math.log(span / m) / Math.LN10)), err = m / span * step; + if (err <= .15) step *= 10; else if (err <= .35) step *= 5; else if (err <= .75) step *= 2; + extent[0] = Math.ceil(extent[0] / step) * step; + extent[1] = Math.floor(extent[1] / step) * step + step * .5; + extent[2] = step; + return extent; + } + function d3_scale_linearTicks(domain, m) { + return d3.range.apply(d3, d3_scale_linearTickRange(domain, m)); + } + function d3_scale_linearTickFormat(domain, m, format) { + var range = d3_scale_linearTickRange(domain, m); + if (format) { + var match = d3_format_re.exec(format); + match.shift(); + if (match[8] === "s") { + var prefix = d3.formatPrefix(Math.max(abs(range[0]), abs(range[1]))); + if (!match[7]) match[7] = "." + d3_scale_linearPrecision(prefix.scale(range[2])); + match[8] = "f"; + format = d3.format(match.join("")); + return function(d) { + return format(prefix.scale(d)) + prefix.symbol; + }; + } + if (!match[7]) match[7] = "." + d3_scale_linearFormatPrecision(match[8], range); + format = match.join(""); + } else { + format = ",." + d3_scale_linearPrecision(range[2]) + "f"; + } + return d3.format(format); + } + var d3_scale_linearFormatSignificant = { + s: 1, + g: 1, + p: 1, + r: 1, + e: 1 + }; + function d3_scale_linearPrecision(value) { + return -Math.floor(Math.log(value) / Math.LN10 + .01); + } + function d3_scale_linearFormatPrecision(type, range) { + var p = d3_scale_linearPrecision(range[2]); + return type in d3_scale_linearFormatSignificant ? Math.abs(p - d3_scale_linearPrecision(Math.max(abs(range[0]), abs(range[1])))) + +(type !== "e") : p - (type === "%") * 2; + } + d3.scale.log = function() { + return d3_scale_log(d3.scale.linear().domain([ 0, 1 ]), 10, true, [ 1, 10 ]); + }; + function d3_scale_log(linear, base, positive, domain) { + function log(x) { + return (positive ? Math.log(x < 0 ? 0 : x) : -Math.log(x > 0 ? 0 : -x)) / Math.log(base); + } + function pow(x) { + return positive ? Math.pow(base, x) : -Math.pow(base, -x); + } + function scale(x) { + return linear(log(x)); + } + scale.invert = function(x) { + return pow(linear.invert(x)); + }; + scale.domain = function(x) { + if (!arguments.length) return domain; + positive = x[0] >= 0; + linear.domain((domain = x.map(Number)).map(log)); + return scale; + }; + scale.base = function(_) { + if (!arguments.length) return base; + base = +_; + linear.domain(domain.map(log)); + return scale; + }; + scale.nice = function() { + var niced = d3_scale_nice(domain.map(log), positive ? Math : d3_scale_logNiceNegative); + linear.domain(niced); + domain = niced.map(pow); + return scale; + }; + scale.ticks = function() { + var extent = d3_scaleExtent(domain), ticks = [], u = extent[0], v = extent[1], i = Math.floor(log(u)), j = Math.ceil(log(v)), n = base % 1 ? 2 : base; + if (isFinite(j - i)) { + if (positive) { + for (;i < j; i++) for (var k = 1; k < n; k++) ticks.push(pow(i) * k); + ticks.push(pow(i)); + } else { + ticks.push(pow(i)); + for (;i++ < j; ) for (var k = n - 1; k > 0; k--) ticks.push(pow(i) * k); + } + for (i = 0; ticks[i] < u; i++) {} + for (j = ticks.length; ticks[j - 1] > v; j--) {} + ticks = ticks.slice(i, j); + } + return ticks; + }; + scale.tickFormat = function(n, format) { + if (!arguments.length) return d3_scale_logFormat; + if (arguments.length < 2) format = d3_scale_logFormat; else if (typeof format !== "function") format = d3.format(format); + var k = Math.max(.1, n / scale.ticks().length), f = positive ? (e = 1e-12, Math.ceil) : (e = -1e-12, + Math.floor), e; + return function(d) { + return d / pow(f(log(d) + e)) <= k ? format(d) : ""; + }; + }; + scale.copy = function() { + return d3_scale_log(linear.copy(), base, positive, domain); + }; + return d3_scale_linearRebind(scale, linear); + } + var d3_scale_logFormat = d3.format(".0e"), d3_scale_logNiceNegative = { + floor: function(x) { + return -Math.ceil(-x); + }, + ceil: function(x) { + return -Math.floor(-x); + } + }; + d3.scale.pow = function() { + return d3_scale_pow(d3.scale.linear(), 1, [ 0, 1 ]); + }; + function d3_scale_pow(linear, exponent, domain) { + var powp = d3_scale_powPow(exponent), powb = d3_scale_powPow(1 / exponent); + function scale(x) { + return linear(powp(x)); + } + scale.invert = function(x) { + return powb(linear.invert(x)); + }; + scale.domain = function(x) { + if (!arguments.length) return domain; + linear.domain((domain = x.map(Number)).map(powp)); + return scale; + }; + scale.ticks = function(m) { + return d3_scale_linearTicks(domain, m); + }; + scale.tickFormat = function(m, format) { + return d3_scale_linearTickFormat(domain, m, format); + }; + scale.nice = function(m) { + return scale.domain(d3_scale_linearNice(domain, m)); + }; + scale.exponent = function(x) { + if (!arguments.length) return exponent; + powp = d3_scale_powPow(exponent = x); + powb = d3_scale_powPow(1 / exponent); + linear.domain(domain.map(powp)); + return scale; + }; + scale.copy = function() { + return d3_scale_pow(linear.copy(), exponent, domain); + }; + return d3_scale_linearRebind(scale, linear); + } + function d3_scale_powPow(e) { + return function(x) { + return x < 0 ? -Math.pow(-x, e) : Math.pow(x, e); + }; + } + d3.scale.sqrt = function() { + return d3.scale.pow().exponent(.5); + }; + d3.scale.ordinal = function() { + return d3_scale_ordinal([], { + t: "range", + a: [ [] ] + }); + }; + function d3_scale_ordinal(domain, ranger) { + var index, range, rangeBand; + function scale(x) { + return range[((index.get(x) || (ranger.t === "range" ? index.set(x, domain.push(x)) : NaN)) - 1) % range.length]; + } + function steps(start, step) { + return d3.range(domain.length).map(function(i) { + return start + step * i; + }); + } + scale.domain = function(x) { + if (!arguments.length) return domain; + domain = []; + index = new d3_Map(); + var i = -1, n = x.length, xi; + while (++i < n) if (!index.has(xi = x[i])) index.set(xi, domain.push(xi)); + return scale[ranger.t].apply(scale, ranger.a); + }; + scale.range = function(x) { + if (!arguments.length) return range; + range = x; + rangeBand = 0; + ranger = { + t: "range", + a: arguments + }; + return scale; + }; + scale.rangePoints = function(x, padding) { + if (arguments.length < 2) padding = 0; + var start = x[0], stop = x[1], step = (stop - start) / (Math.max(1, domain.length - 1) + padding); + range = steps(domain.length < 2 ? (start + stop) / 2 : start + step * padding / 2, step); + rangeBand = 0; + ranger = { + t: "rangePoints", + a: arguments + }; + return scale; + }; + scale.rangeBands = function(x, padding, outerPadding) { + if (arguments.length < 2) padding = 0; + if (arguments.length < 3) outerPadding = padding; + var reverse = x[1] < x[0], start = x[reverse - 0], stop = x[1 - reverse], step = (stop - start) / (domain.length - padding + 2 * outerPadding); + range = steps(start + step * outerPadding, step); + if (reverse) range.reverse(); + rangeBand = step * (1 - padding); + ranger = { + t: "rangeBands", + a: arguments + }; + return scale; + }; + scale.rangeRoundBands = function(x, padding, outerPadding) { + if (arguments.length < 2) padding = 0; + if (arguments.length < 3) outerPadding = padding; + var reverse = x[1] < x[0], start = x[reverse - 0], stop = x[1 - reverse], step = Math.floor((stop - start) / (domain.length - padding + 2 * outerPadding)), error = stop - start - (domain.length - padding) * step; + range = steps(start + Math.round(error / 2), step); + if (reverse) range.reverse(); + rangeBand = Math.round(step * (1 - padding)); + ranger = { + t: "rangeRoundBands", + a: arguments + }; + return scale; + }; + scale.rangeBand = function() { + return rangeBand; + }; + scale.rangeExtent = function() { + return d3_scaleExtent(ranger.a[0]); + }; + scale.copy = function() { + return d3_scale_ordinal(domain, ranger); + }; + return scale.domain(domain); + } + d3.scale.category10 = function() { + return d3.scale.ordinal().range(d3_category10); + }; + d3.scale.category20 = function() { + return d3.scale.ordinal().range(d3_category20); + }; + d3.scale.category20b = function() { + return d3.scale.ordinal().range(d3_category20b); + }; + d3.scale.category20c = function() { + return d3.scale.ordinal().range(d3_category20c); + }; + var d3_category10 = [ 2062260, 16744206, 2924588, 14034728, 9725885, 9197131, 14907330, 8355711, 12369186, 1556175 ].map(d3_rgbString); + var d3_category20 = [ 2062260, 11454440, 16744206, 16759672, 2924588, 10018698, 14034728, 16750742, 9725885, 12955861, 9197131, 12885140, 14907330, 16234194, 8355711, 13092807, 12369186, 14408589, 1556175, 10410725 ].map(d3_rgbString); + var d3_category20b = [ 3750777, 5395619, 7040719, 10264286, 6519097, 9216594, 11915115, 13556636, 9202993, 12426809, 15186514, 15190932, 8666169, 11356490, 14049643, 15177372, 8077683, 10834324, 13528509, 14589654 ].map(d3_rgbString); + var d3_category20c = [ 3244733, 7057110, 10406625, 13032431, 15095053, 16616764, 16625259, 16634018, 3253076, 7652470, 10607003, 13101504, 7695281, 10394312, 12369372, 14342891, 6513507, 9868950, 12434877, 14277081 ].map(d3_rgbString); + d3.scale.quantile = function() { + return d3_scale_quantile([], []); + }; + function d3_scale_quantile(domain, range) { + var thresholds; + function rescale() { + var k = 0, q = range.length; + thresholds = []; + while (++k < q) thresholds[k - 1] = d3.quantile(domain, k / q); + return scale; + } + function scale(x) { + if (!isNaN(x = +x)) return range[d3.bisect(thresholds, x)]; + } + scale.domain = function(x) { + if (!arguments.length) return domain; + domain = x.filter(d3_number).sort(d3_ascending); + return rescale(); + }; + scale.range = function(x) { + if (!arguments.length) return range; + range = x; + return rescale(); + }; + scale.quantiles = function() { + return thresholds; + }; + scale.invertExtent = function(y) { + y = range.indexOf(y); + return y < 0 ? [ NaN, NaN ] : [ y > 0 ? thresholds[y - 1] : domain[0], y < thresholds.length ? thresholds[y] : domain[domain.length - 1] ]; + }; + scale.copy = function() { + return d3_scale_quantile(domain, range); + }; + return rescale(); + } + d3.scale.quantize = function() { + return d3_scale_quantize(0, 1, [ 0, 1 ]); + }; + function d3_scale_quantize(x0, x1, range) { + var kx, i; + function scale(x) { + return range[Math.max(0, Math.min(i, Math.floor(kx * (x - x0))))]; + } + function rescale() { + kx = range.length / (x1 - x0); + i = range.length - 1; + return scale; + } + scale.domain = function(x) { + if (!arguments.length) return [ x0, x1 ]; + x0 = +x[0]; + x1 = +x[x.length - 1]; + return rescale(); + }; + scale.range = function(x) { + if (!arguments.length) return range; + range = x; + return rescale(); + }; + scale.invertExtent = function(y) { + y = range.indexOf(y); + y = y < 0 ? NaN : y / kx + x0; + return [ y, y + 1 / kx ]; + }; + scale.copy = function() { + return d3_scale_quantize(x0, x1, range); + }; + return rescale(); + } + d3.scale.threshold = function() { + return d3_scale_threshold([ .5 ], [ 0, 1 ]); + }; + function d3_scale_threshold(domain, range) { + function scale(x) { + if (x <= x) return range[d3.bisect(domain, x)]; + } + scale.domain = function(_) { + if (!arguments.length) return domain; + domain = _; + return scale; + }; + scale.range = function(_) { + if (!arguments.length) return range; + range = _; + return scale; + }; + scale.invertExtent = function(y) { + y = range.indexOf(y); + return [ domain[y - 1], domain[y] ]; + }; + scale.copy = function() { + return d3_scale_threshold(domain, range); + }; + return scale; + } + d3.scale.identity = function() { + return d3_scale_identity([ 0, 1 ]); + }; + function d3_scale_identity(domain) { + function identity(x) { + return +x; + } + identity.invert = identity; + identity.domain = identity.range = function(x) { + if (!arguments.length) return domain; + domain = x.map(identity); + return identity; + }; + identity.ticks = function(m) { + return d3_scale_linearTicks(domain, m); + }; + identity.tickFormat = function(m, format) { + return d3_scale_linearTickFormat(domain, m, format); + }; + identity.copy = function() { + return d3_scale_identity(domain); + }; + return identity; + } + d3.svg = {}; + d3.svg.arc = function() { + var innerRadius = d3_svg_arcInnerRadius, outerRadius = d3_svg_arcOuterRadius, startAngle = d3_svg_arcStartAngle, endAngle = d3_svg_arcEndAngle; + function arc() { + var r0 = innerRadius.apply(this, arguments), r1 = outerRadius.apply(this, arguments), a0 = startAngle.apply(this, arguments) + d3_svg_arcOffset, a1 = endAngle.apply(this, arguments) + d3_svg_arcOffset, da = (a1 < a0 && (da = a0, + a0 = a1, a1 = da), a1 - a0), df = da < π ? "0" : "1", c0 = Math.cos(a0), s0 = Math.sin(a0), c1 = Math.cos(a1), s1 = Math.sin(a1); + return da >= d3_svg_arcMax ? r0 ? "M0," + r1 + "A" + r1 + "," + r1 + " 0 1,1 0," + -r1 + "A" + r1 + "," + r1 + " 0 1,1 0," + r1 + "M0," + r0 + "A" + r0 + "," + r0 + " 0 1,0 0," + -r0 + "A" + r0 + "," + r0 + " 0 1,0 0," + r0 + "Z" : "M0," + r1 + "A" + r1 + "," + r1 + " 0 1,1 0," + -r1 + "A" + r1 + "," + r1 + " 0 1,1 0," + r1 + "Z" : r0 ? "M" + r1 * c0 + "," + r1 * s0 + "A" + r1 + "," + r1 + " 0 " + df + ",1 " + r1 * c1 + "," + r1 * s1 + "L" + r0 * c1 + "," + r0 * s1 + "A" + r0 + "," + r0 + " 0 " + df + ",0 " + r0 * c0 + "," + r0 * s0 + "Z" : "M" + r1 * c0 + "," + r1 * s0 + "A" + r1 + "," + r1 + " 0 " + df + ",1 " + r1 * c1 + "," + r1 * s1 + "L0,0" + "Z"; + } + arc.innerRadius = function(v) { + if (!arguments.length) return innerRadius; + innerRadius = d3_functor(v); + return arc; + }; + arc.outerRadius = function(v) { + if (!arguments.length) return outerRadius; + outerRadius = d3_functor(v); + return arc; + }; + arc.startAngle = function(v) { + if (!arguments.length) return startAngle; + startAngle = d3_functor(v); + return arc; + }; + arc.endAngle = function(v) { + if (!arguments.length) return endAngle; + endAngle = d3_functor(v); + return arc; + }; + arc.centroid = function() { + var r = (innerRadius.apply(this, arguments) + outerRadius.apply(this, arguments)) / 2, a = (startAngle.apply(this, arguments) + endAngle.apply(this, arguments)) / 2 + d3_svg_arcOffset; + return [ Math.cos(a) * r, Math.sin(a) * r ]; + }; + return arc; + }; + var d3_svg_arcOffset = -halfπ, d3_svg_arcMax = τ - ε; + function d3_svg_arcInnerRadius(d) { + return d.innerRadius; + } + function d3_svg_arcOuterRadius(d) { + return d.outerRadius; + } + function d3_svg_arcStartAngle(d) { + return d.startAngle; + } + function d3_svg_arcEndAngle(d) { + return d.endAngle; + } + function d3_svg_line(projection) { + var x = d3_geom_pointX, y = d3_geom_pointY, defined = d3_true, interpolate = d3_svg_lineLinear, interpolateKey = interpolate.key, tension = .7; + function line(data) { + var segments = [], points = [], i = -1, n = data.length, d, fx = d3_functor(x), fy = d3_functor(y); + function segment() { + segments.push("M", interpolate(projection(points), tension)); + } + while (++i < n) { + if (defined.call(this, d = data[i], i)) { + points.push([ +fx.call(this, d, i), +fy.call(this, d, i) ]); + } else if (points.length) { + segment(); + points = []; + } + } + if (points.length) segment(); + return segments.length ? segments.join("") : null; + } + line.x = function(_) { + if (!arguments.length) return x; + x = _; + return line; + }; + line.y = function(_) { + if (!arguments.length) return y; + y = _; + return line; + }; + line.defined = function(_) { + if (!arguments.length) return defined; + defined = _; + return line; + }; + line.interpolate = function(_) { + if (!arguments.length) return interpolateKey; + if (typeof _ === "function") interpolateKey = interpolate = _; else interpolateKey = (interpolate = d3_svg_lineInterpolators.get(_) || d3_svg_lineLinear).key; + return line; + }; + line.tension = function(_) { + if (!arguments.length) return tension; + tension = _; + return line; + }; + return line; + } + d3.svg.line = function() { + return d3_svg_line(d3_identity); + }; + var d3_svg_lineInterpolators = d3.map({ + linear: d3_svg_lineLinear, + "linear-closed": d3_svg_lineLinearClosed, + step: d3_svg_lineStep, + "step-before": d3_svg_lineStepBefore, + "step-after": d3_svg_lineStepAfter, + basis: d3_svg_lineBasis, + "basis-open": d3_svg_lineBasisOpen, + "basis-closed": d3_svg_lineBasisClosed, + bundle: d3_svg_lineBundle, + cardinal: d3_svg_lineCardinal, + "cardinal-open": d3_svg_lineCardinalOpen, + "cardinal-closed": d3_svg_lineCardinalClosed, + monotone: d3_svg_lineMonotone + }); + d3_svg_lineInterpolators.forEach(function(key, value) { + value.key = key; + value.closed = /-closed$/.test(key); + }); + function d3_svg_lineLinear(points) { + return points.join("L"); + } + function d3_svg_lineLinearClosed(points) { + return d3_svg_lineLinear(points) + "Z"; + } + function d3_svg_lineStep(points) { + var i = 0, n = points.length, p = points[0], path = [ p[0], ",", p[1] ]; + while (++i < n) path.push("H", (p[0] + (p = points[i])[0]) / 2, "V", p[1]); + if (n > 1) path.push("H", p[0]); + return path.join(""); + } + function d3_svg_lineStepBefore(points) { + var i = 0, n = points.length, p = points[0], path = [ p[0], ",", p[1] ]; + while (++i < n) path.push("V", (p = points[i])[1], "H", p[0]); + return path.join(""); + } + function d3_svg_lineStepAfter(points) { + var i = 0, n = points.length, p = points[0], path = [ p[0], ",", p[1] ]; + while (++i < n) path.push("H", (p = points[i])[0], "V", p[1]); + return path.join(""); + } + function d3_svg_lineCardinalOpen(points, tension) { + return points.length < 4 ? d3_svg_lineLinear(points) : points[1] + d3_svg_lineHermite(points.slice(1, points.length - 1), d3_svg_lineCardinalTangents(points, tension)); + } + function d3_svg_lineCardinalClosed(points, tension) { + return points.length < 3 ? d3_svg_lineLinear(points) : points[0] + d3_svg_lineHermite((points.push(points[0]), + points), d3_svg_lineCardinalTangents([ points[points.length - 2] ].concat(points, [ points[1] ]), tension)); + } + function d3_svg_lineCardinal(points, tension) { + return points.length < 3 ? d3_svg_lineLinear(points) : points[0] + d3_svg_lineHermite(points, d3_svg_lineCardinalTangents(points, tension)); + } + function d3_svg_lineHermite(points, tangents) { + if (tangents.length < 1 || points.length != tangents.length && points.length != tangents.length + 2) { + return d3_svg_lineLinear(points); + } + var quad = points.length != tangents.length, path = "", p0 = points[0], p = points[1], t0 = tangents[0], t = t0, pi = 1; + if (quad) { + path += "Q" + (p[0] - t0[0] * 2 / 3) + "," + (p[1] - t0[1] * 2 / 3) + "," + p[0] + "," + p[1]; + p0 = points[1]; + pi = 2; + } + if (tangents.length > 1) { + t = tangents[1]; + p = points[pi]; + pi++; + path += "C" + (p0[0] + t0[0]) + "," + (p0[1] + t0[1]) + "," + (p[0] - t[0]) + "," + (p[1] - t[1]) + "," + p[0] + "," + p[1]; + for (var i = 2; i < tangents.length; i++, pi++) { + p = points[pi]; + t = tangents[i]; + path += "S" + (p[0] - t[0]) + "," + (p[1] - t[1]) + "," + p[0] + "," + p[1]; + } + } + if (quad) { + var lp = points[pi]; + path += "Q" + (p[0] + t[0] * 2 / 3) + "," + (p[1] + t[1] * 2 / 3) + "," + lp[0] + "," + lp[1]; + } + return path; + } + function d3_svg_lineCardinalTangents(points, tension) { + var tangents = [], a = (1 - tension) / 2, p0, p1 = points[0], p2 = points[1], i = 1, n = points.length; + while (++i < n) { + p0 = p1; + p1 = p2; + p2 = points[i]; + tangents.push([ a * (p2[0] - p0[0]), a * (p2[1] - p0[1]) ]); + } + return tangents; + } + function d3_svg_lineBasis(points) { + if (points.length < 3) return d3_svg_lineLinear(points); + var i = 1, n = points.length, pi = points[0], x0 = pi[0], y0 = pi[1], px = [ x0, x0, x0, (pi = points[1])[0] ], py = [ y0, y0, y0, pi[1] ], path = [ x0, ",", y0, "L", d3_svg_lineDot4(d3_svg_lineBasisBezier3, px), ",", d3_svg_lineDot4(d3_svg_lineBasisBezier3, py) ]; + points.push(points[n - 1]); + while (++i <= n) { + pi = points[i]; + px.shift(); + px.push(pi[0]); + py.shift(); + py.push(pi[1]); + d3_svg_lineBasisBezier(path, px, py); + } + points.pop(); + path.push("L", pi); + return path.join(""); + } + function d3_svg_lineBasisOpen(points) { + if (points.length < 4) return d3_svg_lineLinear(points); + var path = [], i = -1, n = points.length, pi, px = [ 0 ], py = [ 0 ]; + while (++i < 3) { + pi = points[i]; + px.push(pi[0]); + py.push(pi[1]); + } + path.push(d3_svg_lineDot4(d3_svg_lineBasisBezier3, px) + "," + d3_svg_lineDot4(d3_svg_lineBasisBezier3, py)); + --i; + while (++i < n) { + pi = points[i]; + px.shift(); + px.push(pi[0]); + py.shift(); + py.push(pi[1]); + d3_svg_lineBasisBezier(path, px, py); + } + return path.join(""); + } + function d3_svg_lineBasisClosed(points) { + var path, i = -1, n = points.length, m = n + 4, pi, px = [], py = []; + while (++i < 4) { + pi = points[i % n]; + px.push(pi[0]); + py.push(pi[1]); + } + path = [ d3_svg_lineDot4(d3_svg_lineBasisBezier3, px), ",", d3_svg_lineDot4(d3_svg_lineBasisBezier3, py) ]; + --i; + while (++i < m) { + pi = points[i % n]; + px.shift(); + px.push(pi[0]); + py.shift(); + py.push(pi[1]); + d3_svg_lineBasisBezier(path, px, py); + } + return path.join(""); + } + function d3_svg_lineBundle(points, tension) { + var n = points.length - 1; + if (n) { + var x0 = points[0][0], y0 = points[0][1], dx = points[n][0] - x0, dy = points[n][1] - y0, i = -1, p, t; + while (++i <= n) { + p = points[i]; + t = i / n; + p[0] = tension * p[0] + (1 - tension) * (x0 + t * dx); + p[1] = tension * p[1] + (1 - tension) * (y0 + t * dy); + } + } + return d3_svg_lineBasis(points); + } + function d3_svg_lineDot4(a, b) { + return a[0] * b[0] + a[1] * b[1] + a[2] * b[2] + a[3] * b[3]; + } + var d3_svg_lineBasisBezier1 = [ 0, 2 / 3, 1 / 3, 0 ], d3_svg_lineBasisBezier2 = [ 0, 1 / 3, 2 / 3, 0 ], d3_svg_lineBasisBezier3 = [ 0, 1 / 6, 2 / 3, 1 / 6 ]; + function d3_svg_lineBasisBezier(path, x, y) { + path.push("C", d3_svg_lineDot4(d3_svg_lineBasisBezier1, x), ",", d3_svg_lineDot4(d3_svg_lineBasisBezier1, y), ",", d3_svg_lineDot4(d3_svg_lineBasisBezier2, x), ",", d3_svg_lineDot4(d3_svg_lineBasisBezier2, y), ",", d3_svg_lineDot4(d3_svg_lineBasisBezier3, x), ",", d3_svg_lineDot4(d3_svg_lineBasisBezier3, y)); + } + function d3_svg_lineSlope(p0, p1) { + return (p1[1] - p0[1]) / (p1[0] - p0[0]); + } + function d3_svg_lineFiniteDifferences(points) { + var i = 0, j = points.length - 1, m = [], p0 = points[0], p1 = points[1], d = m[0] = d3_svg_lineSlope(p0, p1); + while (++i < j) { + m[i] = (d + (d = d3_svg_lineSlope(p0 = p1, p1 = points[i + 1]))) / 2; + } + m[i] = d; + return m; + } + function d3_svg_lineMonotoneTangents(points) { + var tangents = [], d, a, b, s, m = d3_svg_lineFiniteDifferences(points), i = -1, j = points.length - 1; + while (++i < j) { + d = d3_svg_lineSlope(points[i], points[i + 1]); + if (abs(d) < ε) { + m[i] = m[i + 1] = 0; + } else { + a = m[i] / d; + b = m[i + 1] / d; + s = a * a + b * b; + if (s > 9) { + s = d * 3 / Math.sqrt(s); + m[i] = s * a; + m[i + 1] = s * b; + } + } + } + i = -1; + while (++i <= j) { + s = (points[Math.min(j, i + 1)][0] - points[Math.max(0, i - 1)][0]) / (6 * (1 + m[i] * m[i])); + tangents.push([ s || 0, m[i] * s || 0 ]); + } + return tangents; + } + function d3_svg_lineMonotone(points) { + return points.length < 3 ? d3_svg_lineLinear(points) : points[0] + d3_svg_lineHermite(points, d3_svg_lineMonotoneTangents(points)); + } + d3.svg.line.radial = function() { + var line = d3_svg_line(d3_svg_lineRadial); + line.radius = line.x, delete line.x; + line.angle = line.y, delete line.y; + return line; + }; + function d3_svg_lineRadial(points) { + var point, i = -1, n = points.length, r, a; + while (++i < n) { + point = points[i]; + r = point[0]; + a = point[1] + d3_svg_arcOffset; + point[0] = r * Math.cos(a); + point[1] = r * Math.sin(a); + } + return points; + } + function d3_svg_area(projection) { + var x0 = d3_geom_pointX, x1 = d3_geom_pointX, y0 = 0, y1 = d3_geom_pointY, defined = d3_true, interpolate = d3_svg_lineLinear, interpolateKey = interpolate.key, interpolateReverse = interpolate, L = "L", tension = .7; + function area(data) { + var segments = [], points0 = [], points1 = [], i = -1, n = data.length, d, fx0 = d3_functor(x0), fy0 = d3_functor(y0), fx1 = x0 === x1 ? function() { + return x; + } : d3_functor(x1), fy1 = y0 === y1 ? function() { + return y; + } : d3_functor(y1), x, y; + function segment() { + segments.push("M", interpolate(projection(points1), tension), L, interpolateReverse(projection(points0.reverse()), tension), "Z"); + } + while (++i < n) { + if (defined.call(this, d = data[i], i)) { + points0.push([ x = +fx0.call(this, d, i), y = +fy0.call(this, d, i) ]); + points1.push([ +fx1.call(this, d, i), +fy1.call(this, d, i) ]); + } else if (points0.length) { + segment(); + points0 = []; + points1 = []; + } + } + if (points0.length) segment(); + return segments.length ? segments.join("") : null; + } + area.x = function(_) { + if (!arguments.length) return x1; + x0 = x1 = _; + return area; + }; + area.x0 = function(_) { + if (!arguments.length) return x0; + x0 = _; + return area; + }; + area.x1 = function(_) { + if (!arguments.length) return x1; + x1 = _; + return area; + }; + area.y = function(_) { + if (!arguments.length) return y1; + y0 = y1 = _; + return area; + }; + area.y0 = function(_) { + if (!arguments.length) return y0; + y0 = _; + return area; + }; + area.y1 = function(_) { + if (!arguments.length) return y1; + y1 = _; + return area; + }; + area.defined = function(_) { + if (!arguments.length) return defined; + defined = _; + return area; + }; + area.interpolate = function(_) { + if (!arguments.length) return interpolateKey; + if (typeof _ === "function") interpolateKey = interpolate = _; else interpolateKey = (interpolate = d3_svg_lineInterpolators.get(_) || d3_svg_lineLinear).key; + interpolateReverse = interpolate.reverse || interpolate; + L = interpolate.closed ? "M" : "L"; + return area; + }; + area.tension = function(_) { + if (!arguments.length) return tension; + tension = _; + return area; + }; + return area; + } + d3_svg_lineStepBefore.reverse = d3_svg_lineStepAfter; + d3_svg_lineStepAfter.reverse = d3_svg_lineStepBefore; + d3.svg.area = function() { + return d3_svg_area(d3_identity); + }; + d3.svg.area.radial = function() { + var area = d3_svg_area(d3_svg_lineRadial); + area.radius = area.x, delete area.x; + area.innerRadius = area.x0, delete area.x0; + area.outerRadius = area.x1, delete area.x1; + area.angle = area.y, delete area.y; + area.startAngle = area.y0, delete area.y0; + area.endAngle = area.y1, delete area.y1; + return area; + }; + d3.svg.chord = function() { + var source = d3_source, target = d3_target, radius = d3_svg_chordRadius, startAngle = d3_svg_arcStartAngle, endAngle = d3_svg_arcEndAngle; + function chord(d, i) { + var s = subgroup(this, source, d, i), t = subgroup(this, target, d, i); + return "M" + s.p0 + arc(s.r, s.p1, s.a1 - s.a0) + (equals(s, t) ? curve(s.r, s.p1, s.r, s.p0) : curve(s.r, s.p1, t.r, t.p0) + arc(t.r, t.p1, t.a1 - t.a0) + curve(t.r, t.p1, s.r, s.p0)) + "Z"; + } + function subgroup(self, f, d, i) { + var subgroup = f.call(self, d, i), r = radius.call(self, subgroup, i), a0 = startAngle.call(self, subgroup, i) + d3_svg_arcOffset, a1 = endAngle.call(self, subgroup, i) + d3_svg_arcOffset; + return { + r: r, + a0: a0, + a1: a1, + p0: [ r * Math.cos(a0), r * Math.sin(a0) ], + p1: [ r * Math.cos(a1), r * Math.sin(a1) ] + }; + } + function equals(a, b) { + return a.a0 == b.a0 && a.a1 == b.a1; + } + function arc(r, p, a) { + return "A" + r + "," + r + " 0 " + +(a > π) + ",1 " + p; + } + function curve(r0, p0, r1, p1) { + return "Q 0,0 " + p1; + } + chord.radius = function(v) { + if (!arguments.length) return radius; + radius = d3_functor(v); + return chord; + }; + chord.source = function(v) { + if (!arguments.length) return source; + source = d3_functor(v); + return chord; + }; + chord.target = function(v) { + if (!arguments.length) return target; + target = d3_functor(v); + return chord; + }; + chord.startAngle = function(v) { + if (!arguments.length) return startAngle; + startAngle = d3_functor(v); + return chord; + }; + chord.endAngle = function(v) { + if (!arguments.length) return endAngle; + endAngle = d3_functor(v); + return chord; + }; + return chord; + }; + function d3_svg_chordRadius(d) { + return d.radius; + } + d3.svg.diagonal = function() { + var source = d3_source, target = d3_target, projection = d3_svg_diagonalProjection; + function diagonal(d, i) { + var p0 = source.call(this, d, i), p3 = target.call(this, d, i), m = (p0.y + p3.y) / 2, p = [ p0, { + x: p0.x, + y: m + }, { + x: p3.x, + y: m + }, p3 ]; + p = p.map(projection); + return "M" + p[0] + "C" + p[1] + " " + p[2] + " " + p[3]; + } + diagonal.source = function(x) { + if (!arguments.length) return source; + source = d3_functor(x); + return diagonal; + }; + diagonal.target = function(x) { + if (!arguments.length) return target; + target = d3_functor(x); + return diagonal; + }; + diagonal.projection = function(x) { + if (!arguments.length) return projection; + projection = x; + return diagonal; + }; + return diagonal; + }; + function d3_svg_diagonalProjection(d) { + return [ d.x, d.y ]; + } + d3.svg.diagonal.radial = function() { + var diagonal = d3.svg.diagonal(), projection = d3_svg_diagonalProjection, projection_ = diagonal.projection; + diagonal.projection = function(x) { + return arguments.length ? projection_(d3_svg_diagonalRadialProjection(projection = x)) : projection; + }; + return diagonal; + }; + function d3_svg_diagonalRadialProjection(projection) { + return function() { + var d = projection.apply(this, arguments), r = d[0], a = d[1] + d3_svg_arcOffset; + return [ r * Math.cos(a), r * Math.sin(a) ]; + }; + } + d3.svg.symbol = function() { + var type = d3_svg_symbolType, size = d3_svg_symbolSize; + function symbol(d, i) { + return (d3_svg_symbols.get(type.call(this, d, i)) || d3_svg_symbolCircle)(size.call(this, d, i)); + } + symbol.type = function(x) { + if (!arguments.length) return type; + type = d3_functor(x); + return symbol; + }; + symbol.size = function(x) { + if (!arguments.length) return size; + size = d3_functor(x); + return symbol; + }; + return symbol; + }; + function d3_svg_symbolSize() { + return 64; + } + function d3_svg_symbolType() { + return "circle"; + } + function d3_svg_symbolCircle(size) { + var r = Math.sqrt(size / π); + return "M0," + r + "A" + r + "," + r + " 0 1,1 0," + -r + "A" + r + "," + r + " 0 1,1 0," + r + "Z"; + } + var d3_svg_symbols = d3.map({ + circle: d3_svg_symbolCircle, + cross: function(size) { + var r = Math.sqrt(size / 5) / 2; + return "M" + -3 * r + "," + -r + "H" + -r + "V" + -3 * r + "H" + r + "V" + -r + "H" + 3 * r + "V" + r + "H" + r + "V" + 3 * r + "H" + -r + "V" + r + "H" + -3 * r + "Z"; + }, + diamond: function(size) { + var ry = Math.sqrt(size / (2 * d3_svg_symbolTan30)), rx = ry * d3_svg_symbolTan30; + return "M0," + -ry + "L" + rx + ",0" + " 0," + ry + " " + -rx + ",0" + "Z"; + }, + square: function(size) { + var r = Math.sqrt(size) / 2; + return "M" + -r + "," + -r + "L" + r + "," + -r + " " + r + "," + r + " " + -r + "," + r + "Z"; + }, + "triangle-down": function(size) { + var rx = Math.sqrt(size / d3_svg_symbolSqrt3), ry = rx * d3_svg_symbolSqrt3 / 2; + return "M0," + ry + "L" + rx + "," + -ry + " " + -rx + "," + -ry + "Z"; + }, + "triangle-up": function(size) { + var rx = Math.sqrt(size / d3_svg_symbolSqrt3), ry = rx * d3_svg_symbolSqrt3 / 2; + return "M0," + -ry + "L" + rx + "," + ry + " " + -rx + "," + ry + "Z"; + } + }); + d3.svg.symbolTypes = d3_svg_symbols.keys(); + var d3_svg_symbolSqrt3 = Math.sqrt(3), d3_svg_symbolTan30 = Math.tan(30 * d3_radians); + function d3_transition(groups, id) { + d3_subclass(groups, d3_transitionPrototype); + groups.id = id; + return groups; + } + var d3_transitionPrototype = [], d3_transitionId = 0, d3_transitionInheritId, d3_transitionInherit; + d3_transitionPrototype.call = d3_selectionPrototype.call; + d3_transitionPrototype.empty = d3_selectionPrototype.empty; + d3_transitionPrototype.node = d3_selectionPrototype.node; + d3_transitionPrototype.size = d3_selectionPrototype.size; + d3.transition = function(selection) { + return arguments.length ? d3_transitionInheritId ? selection.transition() : selection : d3_selectionRoot.transition(); + }; + d3.transition.prototype = d3_transitionPrototype; + d3_transitionPrototype.select = function(selector) { + var id = this.id, subgroups = [], subgroup, subnode, node; + selector = d3_selection_selector(selector); + for (var j = -1, m = this.length; ++j < m; ) { + subgroups.push(subgroup = []); + for (var group = this[j], i = -1, n = group.length; ++i < n; ) { + if ((node = group[i]) && (subnode = selector.call(node, node.__data__, i, j))) { + if ("__data__" in node) subnode.__data__ = node.__data__; + d3_transitionNode(subnode, i, id, node.__transition__[id]); + subgroup.push(subnode); + } else { + subgroup.push(null); + } + } + } + return d3_transition(subgroups, id); + }; + d3_transitionPrototype.selectAll = function(selector) { + var id = this.id, subgroups = [], subgroup, subnodes, node, subnode, transition; + selector = d3_selection_selectorAll(selector); + for (var j = -1, m = this.length; ++j < m; ) { + for (var group = this[j], i = -1, n = group.length; ++i < n; ) { + if (node = group[i]) { + transition = node.__transition__[id]; + subnodes = selector.call(node, node.__data__, i, j); + subgroups.push(subgroup = []); + for (var k = -1, o = subnodes.length; ++k < o; ) { + if (subnode = subnodes[k]) d3_transitionNode(subnode, k, id, transition); + subgroup.push(subnode); + } + } + } + } + return d3_transition(subgroups, id); + }; + d3_transitionPrototype.filter = function(filter) { + var subgroups = [], subgroup, group, node; + if (typeof filter !== "function") filter = d3_selection_filter(filter); + for (var j = 0, m = this.length; j < m; j++) { + subgroups.push(subgroup = []); + for (var group = this[j], i = 0, n = group.length; i < n; i++) { + if ((node = group[i]) && filter.call(node, node.__data__, i, j)) { + subgroup.push(node); + } + } + } + return d3_transition(subgroups, this.id); + }; + d3_transitionPrototype.tween = function(name, tween) { + var id = this.id; + if (arguments.length < 2) return this.node().__transition__[id].tween.get(name); + return d3_selection_each(this, tween == null ? function(node) { + node.__transition__[id].tween.remove(name); + } : function(node) { + node.__transition__[id].tween.set(name, tween); + }); + }; + function d3_transition_tween(groups, name, value, tween) { + var id = groups.id; + return d3_selection_each(groups, typeof value === "function" ? function(node, i, j) { + node.__transition__[id].tween.set(name, tween(value.call(node, node.__data__, i, j))); + } : (value = tween(value), function(node) { + node.__transition__[id].tween.set(name, value); + })); + } + d3_transitionPrototype.attr = function(nameNS, value) { + if (arguments.length < 2) { + for (value in nameNS) this.attr(value, nameNS[value]); + return this; + } + var interpolate = nameNS == "transform" ? d3_interpolateTransform : d3_interpolate, name = d3.ns.qualify(nameNS); + function attrNull() { + this.removeAttribute(name); + } + function attrNullNS() { + this.removeAttributeNS(name.space, name.local); + } + function attrTween(b) { + return b == null ? attrNull : (b += "", function() { + var a = this.getAttribute(name), i; + return a !== b && (i = interpolate(a, b), function(t) { + this.setAttribute(name, i(t)); + }); + }); + } + function attrTweenNS(b) { + return b == null ? attrNullNS : (b += "", function() { + var a = this.getAttributeNS(name.space, name.local), i; + return a !== b && (i = interpolate(a, b), function(t) { + this.setAttributeNS(name.space, name.local, i(t)); + }); + }); + } + return d3_transition_tween(this, "attr." + nameNS, value, name.local ? attrTweenNS : attrTween); + }; + d3_transitionPrototype.attrTween = function(nameNS, tween) { + var name = d3.ns.qualify(nameNS); + function attrTween(d, i) { + var f = tween.call(this, d, i, this.getAttribute(name)); + return f && function(t) { + this.setAttribute(name, f(t)); + }; + } + function attrTweenNS(d, i) { + var f = tween.call(this, d, i, this.getAttributeNS(name.space, name.local)); + return f && function(t) { + this.setAttributeNS(name.space, name.local, f(t)); + }; + } + return this.tween("attr." + nameNS, name.local ? attrTweenNS : attrTween); + }; + d3_transitionPrototype.style = function(name, value, priority) { + var n = arguments.length; + if (n < 3) { + if (typeof name !== "string") { + if (n < 2) value = ""; + for (priority in name) this.style(priority, name[priority], value); + return this; + } + priority = ""; + } + function styleNull() { + this.style.removeProperty(name); + } + function styleString(b) { + return b == null ? styleNull : (b += "", function() { + var a = d3_window.getComputedStyle(this, null).getPropertyValue(name), i; + return a !== b && (i = d3_interpolate(a, b), function(t) { + this.style.setProperty(name, i(t), priority); + }); + }); + } + return d3_transition_tween(this, "style." + name, value, styleString); + }; + d3_transitionPrototype.styleTween = function(name, tween, priority) { + if (arguments.length < 3) priority = ""; + function styleTween(d, i) { + var f = tween.call(this, d, i, d3_window.getComputedStyle(this, null).getPropertyValue(name)); + return f && function(t) { + this.style.setProperty(name, f(t), priority); + }; + } + return this.tween("style." + name, styleTween); + }; + d3_transitionPrototype.text = function(value) { + return d3_transition_tween(this, "text", value, d3_transition_text); + }; + function d3_transition_text(b) { + if (b == null) b = ""; + return function() { + this.textContent = b; + }; + } + d3_transitionPrototype.remove = function() { + return this.each("end.transition", function() { + var p; + if (this.__transition__.count < 2 && (p = this.parentNode)) p.removeChild(this); + }); + }; + d3_transitionPrototype.ease = function(value) { + var id = this.id; + if (arguments.length < 1) return this.node().__transition__[id].ease; + if (typeof value !== "function") value = d3.ease.apply(d3, arguments); + return d3_selection_each(this, function(node) { + node.__transition__[id].ease = value; + }); + }; + d3_transitionPrototype.delay = function(value) { + var id = this.id; + if (arguments.length < 1) return this.node().__transition__[id].delay; + return d3_selection_each(this, typeof value === "function" ? function(node, i, j) { + node.__transition__[id].delay = +value.call(node, node.__data__, i, j); + } : (value = +value, function(node) { + node.__transition__[id].delay = value; + })); + }; + d3_transitionPrototype.duration = function(value) { + var id = this.id; + if (arguments.length < 1) return this.node().__transition__[id].duration; + return d3_selection_each(this, typeof value === "function" ? function(node, i, j) { + node.__transition__[id].duration = Math.max(1, value.call(node, node.__data__, i, j)); + } : (value = Math.max(1, value), function(node) { + node.__transition__[id].duration = value; + })); + }; + d3_transitionPrototype.each = function(type, listener) { + var id = this.id; + if (arguments.length < 2) { + var inherit = d3_transitionInherit, inheritId = d3_transitionInheritId; + d3_transitionInheritId = id; + d3_selection_each(this, function(node, i, j) { + d3_transitionInherit = node.__transition__[id]; + type.call(node, node.__data__, i, j); + }); + d3_transitionInherit = inherit; + d3_transitionInheritId = inheritId; + } else { + d3_selection_each(this, function(node) { + var transition = node.__transition__[id]; + (transition.event || (transition.event = d3.dispatch("start", "end"))).on(type, listener); + }); + } + return this; + }; + d3_transitionPrototype.transition = function() { + var id0 = this.id, id1 = ++d3_transitionId, subgroups = [], subgroup, group, node, transition; + for (var j = 0, m = this.length; j < m; j++) { + subgroups.push(subgroup = []); + for (var group = this[j], i = 0, n = group.length; i < n; i++) { + if (node = group[i]) { + transition = Object.create(node.__transition__[id0]); + transition.delay += transition.duration; + d3_transitionNode(node, i, id1, transition); + } + subgroup.push(node); + } + } + return d3_transition(subgroups, id1); + }; + function d3_transitionNode(node, i, id, inherit) { + var lock = node.__transition__ || (node.__transition__ = { + active: 0, + count: 0 + }), transition = lock[id]; + if (!transition) { + var time = inherit.time; + transition = lock[id] = { + tween: new d3_Map(), + time: time, + ease: inherit.ease, + delay: inherit.delay, + duration: inherit.duration + }; + ++lock.count; + d3.timer(function(elapsed) { + var d = node.__data__, ease = transition.ease, delay = transition.delay, duration = transition.duration, timer = d3_timer_active, tweened = []; + timer.t = delay + time; + if (delay <= elapsed) return start(elapsed - delay); + timer.c = start; + function start(elapsed) { + if (lock.active > id) return stop(); + lock.active = id; + transition.event && transition.event.start.call(node, d, i); + transition.tween.forEach(function(key, value) { + if (value = value.call(node, d, i)) { + tweened.push(value); + } + }); + d3.timer(function() { + timer.c = tick(elapsed || 1) ? d3_true : tick; + return 1; + }, 0, time); + } + function tick(elapsed) { + if (lock.active !== id) return stop(); + var t = elapsed / duration, e = ease(t), n = tweened.length; + while (n > 0) { + tweened[--n].call(node, e); + } + if (t >= 1) { + transition.event && transition.event.end.call(node, d, i); + return stop(); + } + } + function stop() { + if (--lock.count) delete lock[id]; else delete node.__transition__; + return 1; + } + }, 0, time); + } + } + d3.svg.axis = function() { + var scale = d3.scale.linear(), orient = d3_svg_axisDefaultOrient, innerTickSize = 6, outerTickSize = 6, tickPadding = 3, tickArguments_ = [ 10 ], tickValues = null, tickFormat_; + function axis(g) { + g.each(function() { + var g = d3.select(this); + var scale0 = this.__chart__ || scale, scale1 = this.__chart__ = scale.copy(); + var ticks = tickValues == null ? scale1.ticks ? scale1.ticks.apply(scale1, tickArguments_) : scale1.domain() : tickValues, tickFormat = tickFormat_ == null ? scale1.tickFormat ? scale1.tickFormat.apply(scale1, tickArguments_) : d3_identity : tickFormat_, tick = g.selectAll(".tick").data(ticks, scale1), tickEnter = tick.enter().insert("g", ".domain").attr("class", "tick").style("opacity", ε), tickExit = d3.transition(tick.exit()).style("opacity", ε).remove(), tickUpdate = d3.transition(tick.order()).style("opacity", 1), tickTransform; + var range = d3_scaleRange(scale1), path = g.selectAll(".domain").data([ 0 ]), pathUpdate = (path.enter().append("path").attr("class", "domain"), + d3.transition(path)); + tickEnter.append("line"); + tickEnter.append("text"); + var lineEnter = tickEnter.select("line"), lineUpdate = tickUpdate.select("line"), text = tick.select("text").text(tickFormat), textEnter = tickEnter.select("text"), textUpdate = tickUpdate.select("text"); + switch (orient) { + case "bottom": + { + tickTransform = d3_svg_axisX; + lineEnter.attr("y2", innerTickSize); + textEnter.attr("y", Math.max(innerTickSize, 0) + tickPadding); + lineUpdate.attr("x2", 0).attr("y2", innerTickSize); + textUpdate.attr("x", 0).attr("y", Math.max(innerTickSize, 0) + tickPadding); + text.attr("dy", ".71em").style("text-anchor", "middle"); + pathUpdate.attr("d", "M" + range[0] + "," + outerTickSize + "V0H" + range[1] + "V" + outerTickSize); + break; + } + + case "top": + { + tickTransform = d3_svg_axisX; + lineEnter.attr("y2", -innerTickSize); + textEnter.attr("y", -(Math.max(innerTickSize, 0) + tickPadding)); + lineUpdate.attr("x2", 0).attr("y2", -innerTickSize); + textUpdate.attr("x", 0).attr("y", -(Math.max(innerTickSize, 0) + tickPadding)); + text.attr("dy", "0em").style("text-anchor", "middle"); + pathUpdate.attr("d", "M" + range[0] + "," + -outerTickSize + "V0H" + range[1] + "V" + -outerTickSize); + break; + } + + case "left": + { + tickTransform = d3_svg_axisY; + lineEnter.attr("x2", -innerTickSize); + textEnter.attr("x", -(Math.max(innerTickSize, 0) + tickPadding)); + lineUpdate.attr("x2", -innerTickSize).attr("y2", 0); + textUpdate.attr("x", -(Math.max(innerTickSize, 0) + tickPadding)).attr("y", 0); + text.attr("dy", ".32em").style("text-anchor", "end"); + pathUpdate.attr("d", "M" + -outerTickSize + "," + range[0] + "H0V" + range[1] + "H" + -outerTickSize); + break; + } + + case "right": + { + tickTransform = d3_svg_axisY; + lineEnter.attr("x2", innerTickSize); + textEnter.attr("x", Math.max(innerTickSize, 0) + tickPadding); + lineUpdate.attr("x2", innerTickSize).attr("y2", 0); + textUpdate.attr("x", Math.max(innerTickSize, 0) + tickPadding).attr("y", 0); + text.attr("dy", ".32em").style("text-anchor", "start"); + pathUpdate.attr("d", "M" + outerTickSize + "," + range[0] + "H0V" + range[1] + "H" + outerTickSize); + break; + } + } + if (scale1.rangeBand) { + var x = scale1, dx = x.rangeBand() / 2; + scale0 = scale1 = function(d) { + return x(d) + dx; + }; + } else if (scale0.rangeBand) { + scale0 = scale1; + } else { + tickExit.call(tickTransform, scale1); + } + tickEnter.call(tickTransform, scale0); + tickUpdate.call(tickTransform, scale1); + }); + } + axis.scale = function(x) { + if (!arguments.length) return scale; + scale = x; + return axis; + }; + axis.orient = function(x) { + if (!arguments.length) return orient; + orient = x in d3_svg_axisOrients ? x + "" : d3_svg_axisDefaultOrient; + return axis; + }; + axis.ticks = function() { + if (!arguments.length) return tickArguments_; + tickArguments_ = arguments; + return axis; + }; + axis.tickValues = function(x) { + if (!arguments.length) return tickValues; + tickValues = x; + return axis; + }; + axis.tickFormat = function(x) { + if (!arguments.length) return tickFormat_; + tickFormat_ = x; + return axis; + }; + axis.tickSize = function(x) { + var n = arguments.length; + if (!n) return innerTickSize; + innerTickSize = +x; + outerTickSize = +arguments[n - 1]; + return axis; + }; + axis.innerTickSize = function(x) { + if (!arguments.length) return innerTickSize; + innerTickSize = +x; + return axis; + }; + axis.outerTickSize = function(x) { + if (!arguments.length) return outerTickSize; + outerTickSize = +x; + return axis; + }; + axis.tickPadding = function(x) { + if (!arguments.length) return tickPadding; + tickPadding = +x; + return axis; + }; + axis.tickSubdivide = function() { + return arguments.length && axis; + }; + return axis; + }; + var d3_svg_axisDefaultOrient = "bottom", d3_svg_axisOrients = { + top: 1, + right: 1, + bottom: 1, + left: 1 + }; + function d3_svg_axisX(selection, x) { + selection.attr("transform", function(d) { + return "translate(" + x(d) + ",0)"; + }); + } + function d3_svg_axisY(selection, y) { + selection.attr("transform", function(d) { + return "translate(0," + y(d) + ")"; + }); + } + d3.svg.brush = function() { + var event = d3_eventDispatch(brush, "brushstart", "brush", "brushend"), x = null, y = null, xExtent = [ 0, 0 ], yExtent = [ 0, 0 ], xExtentDomain, yExtentDomain, xClamp = true, yClamp = true, resizes = d3_svg_brushResizes[0]; + function brush(g) { + g.each(function() { + var g = d3.select(this).style("pointer-events", "all").style("-webkit-tap-highlight-color", "rgba(0,0,0,0)").on("mousedown.brush", brushstart).on("touchstart.brush", brushstart); + var background = g.selectAll(".background").data([ 0 ]); + background.enter().append("rect").attr("class", "background").style("visibility", "hidden").style("cursor", "crosshair"); + g.selectAll(".extent").data([ 0 ]).enter().append("rect").attr("class", "extent").style("cursor", "move"); + var resize = g.selectAll(".resize").data(resizes, d3_identity); + resize.exit().remove(); + resize.enter().append("g").attr("class", function(d) { + return "resize " + d; + }).style("cursor", function(d) { + return d3_svg_brushCursor[d]; + }).append("rect").attr("x", function(d) { + return /[ew]$/.test(d) ? -3 : null; + }).attr("y", function(d) { + return /^[ns]/.test(d) ? -3 : null; + }).attr("width", 6).attr("height", 6).style("visibility", "hidden"); + resize.style("display", brush.empty() ? "none" : null); + var gUpdate = d3.transition(g), backgroundUpdate = d3.transition(background), range; + if (x) { + range = d3_scaleRange(x); + backgroundUpdate.attr("x", range[0]).attr("width", range[1] - range[0]); + redrawX(gUpdate); + } + if (y) { + range = d3_scaleRange(y); + backgroundUpdate.attr("y", range[0]).attr("height", range[1] - range[0]); + redrawY(gUpdate); + } + redraw(gUpdate); + }); + } + brush.event = function(g) { + g.each(function() { + var event_ = event.of(this, arguments), extent1 = { + x: xExtent, + y: yExtent, + i: xExtentDomain, + j: yExtentDomain + }, extent0 = this.__chart__ || extent1; + this.__chart__ = extent1; + if (d3_transitionInheritId) { + d3.select(this).transition().each("start.brush", function() { + xExtentDomain = extent0.i; + yExtentDomain = extent0.j; + xExtent = extent0.x; + yExtent = extent0.y; + event_({ + type: "brushstart" + }); + }).tween("brush:brush", function() { + var xi = d3_interpolateArray(xExtent, extent1.x), yi = d3_interpolateArray(yExtent, extent1.y); + xExtentDomain = yExtentDomain = null; + return function(t) { + xExtent = extent1.x = xi(t); + yExtent = extent1.y = yi(t); + event_({ + type: "brush", + mode: "resize" + }); + }; + }).each("end.brush", function() { + xExtentDomain = extent1.i; + yExtentDomain = extent1.j; + event_({ + type: "brush", + mode: "resize" + }); + event_({ + type: "brushend" + }); + }); + } else { + event_({ + type: "brushstart" + }); + event_({ + type: "brush", + mode: "resize" + }); + event_({ + type: "brushend" + }); + } + }); + }; + function redraw(g) { + g.selectAll(".resize").attr("transform", function(d) { + return "translate(" + xExtent[+/e$/.test(d)] + "," + yExtent[+/^s/.test(d)] + ")"; + }); + } + function redrawX(g) { + g.select(".extent").attr("x", xExtent[0]); + g.selectAll(".extent,.n>rect,.s>rect").attr("width", xExtent[1] - xExtent[0]); + } + function redrawY(g) { + g.select(".extent").attr("y", yExtent[0]); + g.selectAll(".extent,.e>rect,.w>rect").attr("height", yExtent[1] - yExtent[0]); + } + function brushstart() { + var target = this, eventTarget = d3.select(d3.event.target), event_ = event.of(target, arguments), g = d3.select(target), resizing = eventTarget.datum(), resizingX = !/^(n|s)$/.test(resizing) && x, resizingY = !/^(e|w)$/.test(resizing) && y, dragging = eventTarget.classed("extent"), dragRestore = d3_event_dragSuppress(), center, origin = d3.mouse(target), offset; + var w = d3.select(d3_window).on("keydown.brush", keydown).on("keyup.brush", keyup); + if (d3.event.changedTouches) { + w.on("touchmove.brush", brushmove).on("touchend.brush", brushend); + } else { + w.on("mousemove.brush", brushmove).on("mouseup.brush", brushend); + } + g.interrupt().selectAll("*").interrupt(); + if (dragging) { + origin[0] = xExtent[0] - origin[0]; + origin[1] = yExtent[0] - origin[1]; + } else if (resizing) { + var ex = +/w$/.test(resizing), ey = +/^n/.test(resizing); + offset = [ xExtent[1 - ex] - origin[0], yExtent[1 - ey] - origin[1] ]; + origin[0] = xExtent[ex]; + origin[1] = yExtent[ey]; + } else if (d3.event.altKey) center = origin.slice(); + g.style("pointer-events", "none").selectAll(".resize").style("display", null); + d3.select("body").style("cursor", eventTarget.style("cursor")); + event_({ + type: "brushstart" + }); + brushmove(); + function keydown() { + if (d3.event.keyCode == 32) { + if (!dragging) { + center = null; + origin[0] -= xExtent[1]; + origin[1] -= yExtent[1]; + dragging = 2; + } + d3_eventPreventDefault(); + } + } + function keyup() { + if (d3.event.keyCode == 32 && dragging == 2) { + origin[0] += xExtent[1]; + origin[1] += yExtent[1]; + dragging = 0; + d3_eventPreventDefault(); + } + } + function brushmove() { + var point = d3.mouse(target), moved = false; + if (offset) { + point[0] += offset[0]; + point[1] += offset[1]; + } + if (!dragging) { + if (d3.event.altKey) { + if (!center) center = [ (xExtent[0] + xExtent[1]) / 2, (yExtent[0] + yExtent[1]) / 2 ]; + origin[0] = xExtent[+(point[0] < center[0])]; + origin[1] = yExtent[+(point[1] < center[1])]; + } else center = null; + } + if (resizingX && move1(point, x, 0)) { + redrawX(g); + moved = true; + } + if (resizingY && move1(point, y, 1)) { + redrawY(g); + moved = true; + } + if (moved) { + redraw(g); + event_({ + type: "brush", + mode: dragging ? "move" : "resize" + }); + } + } + function move1(point, scale, i) { + var range = d3_scaleRange(scale), r0 = range[0], r1 = range[1], position = origin[i], extent = i ? yExtent : xExtent, size = extent[1] - extent[0], min, max; + if (dragging) { + r0 -= position; + r1 -= size + position; + } + min = (i ? yClamp : xClamp) ? Math.max(r0, Math.min(r1, point[i])) : point[i]; + if (dragging) { + max = (min += position) + size; + } else { + if (center) position = Math.max(r0, Math.min(r1, 2 * center[i] - min)); + if (position < min) { + max = min; + min = position; + } else { + max = position; + } + } + if (extent[0] != min || extent[1] != max) { + if (i) yExtentDomain = null; else xExtentDomain = null; + extent[0] = min; + extent[1] = max; + return true; + } + } + function brushend() { + brushmove(); + g.style("pointer-events", "all").selectAll(".resize").style("display", brush.empty() ? "none" : null); + d3.select("body").style("cursor", null); + w.on("mousemove.brush", null).on("mouseup.brush", null).on("touchmove.brush", null).on("touchend.brush", null).on("keydown.brush", null).on("keyup.brush", null); + dragRestore(); + event_({ + type: "brushend" + }); + } + } + brush.x = function(z) { + if (!arguments.length) return x; + x = z; + resizes = d3_svg_brushResizes[!x << 1 | !y]; + return brush; + }; + brush.y = function(z) { + if (!arguments.length) return y; + y = z; + resizes = d3_svg_brushResizes[!x << 1 | !y]; + return brush; + }; + brush.clamp = function(z) { + if (!arguments.length) return x && y ? [ xClamp, yClamp ] : x ? xClamp : y ? yClamp : null; + if (x && y) xClamp = !!z[0], yClamp = !!z[1]; else if (x) xClamp = !!z; else if (y) yClamp = !!z; + return brush; + }; + brush.extent = function(z) { + var x0, x1, y0, y1, t; + if (!arguments.length) { + if (x) { + if (xExtentDomain) { + x0 = xExtentDomain[0], x1 = xExtentDomain[1]; + } else { + x0 = xExtent[0], x1 = xExtent[1]; + if (x.invert) x0 = x.invert(x0), x1 = x.invert(x1); + if (x1 < x0) t = x0, x0 = x1, x1 = t; + } + } + if (y) { + if (yExtentDomain) { + y0 = yExtentDomain[0], y1 = yExtentDomain[1]; + } else { + y0 = yExtent[0], y1 = yExtent[1]; + if (y.invert) y0 = y.invert(y0), y1 = y.invert(y1); + if (y1 < y0) t = y0, y0 = y1, y1 = t; + } + } + return x && y ? [ [ x0, y0 ], [ x1, y1 ] ] : x ? [ x0, x1 ] : y && [ y0, y1 ]; + } + if (x) { + x0 = z[0], x1 = z[1]; + if (y) x0 = x0[0], x1 = x1[0]; + xExtentDomain = [ x0, x1 ]; + if (x.invert) x0 = x(x0), x1 = x(x1); + if (x1 < x0) t = x0, x0 = x1, x1 = t; + if (x0 != xExtent[0] || x1 != xExtent[1]) xExtent = [ x0, x1 ]; + } + if (y) { + y0 = z[0], y1 = z[1]; + if (x) y0 = y0[1], y1 = y1[1]; + yExtentDomain = [ y0, y1 ]; + if (y.invert) y0 = y(y0), y1 = y(y1); + if (y1 < y0) t = y0, y0 = y1, y1 = t; + if (y0 != yExtent[0] || y1 != yExtent[1]) yExtent = [ y0, y1 ]; + } + return brush; + }; + brush.clear = function() { + if (!brush.empty()) { + xExtent = [ 0, 0 ], yExtent = [ 0, 0 ]; + xExtentDomain = yExtentDomain = null; + } + return brush; + }; + brush.empty = function() { + return !!x && xExtent[0] == xExtent[1] || !!y && yExtent[0] == yExtent[1]; + }; + return d3.rebind(brush, event, "on"); + }; + var d3_svg_brushCursor = { + n: "ns-resize", + e: "ew-resize", + s: "ns-resize", + w: "ew-resize", + nw: "nwse-resize", + ne: "nesw-resize", + se: "nwse-resize", + sw: "nesw-resize" + }; + var d3_svg_brushResizes = [ [ "n", "e", "s", "w", "nw", "ne", "se", "sw" ], [ "e", "w" ], [ "n", "s" ], [] ]; + var d3_time_format = d3_time.format = d3_locale_enUS.timeFormat; + var d3_time_formatUtc = d3_time_format.utc; + var d3_time_formatIso = d3_time_formatUtc("%Y-%m-%dT%H:%M:%S.%LZ"); + d3_time_format.iso = Date.prototype.toISOString && +new Date("2000-01-01T00:00:00.000Z") ? d3_time_formatIsoNative : d3_time_formatIso; + function d3_time_formatIsoNative(date) { + return date.toISOString(); + } + d3_time_formatIsoNative.parse = function(string) { + var date = new Date(string); + return isNaN(date) ? null : date; + }; + d3_time_formatIsoNative.toString = d3_time_formatIso.toString; + d3_time.second = d3_time_interval(function(date) { + return new d3_date(Math.floor(date / 1e3) * 1e3); + }, function(date, offset) { + date.setTime(date.getTime() + Math.floor(offset) * 1e3); + }, function(date) { + return date.getSeconds(); + }); + d3_time.seconds = d3_time.second.range; + d3_time.seconds.utc = d3_time.second.utc.range; + d3_time.minute = d3_time_interval(function(date) { + return new d3_date(Math.floor(date / 6e4) * 6e4); + }, function(date, offset) { + date.setTime(date.getTime() + Math.floor(offset) * 6e4); + }, function(date) { + return date.getMinutes(); + }); + d3_time.minutes = d3_time.minute.range; + d3_time.minutes.utc = d3_time.minute.utc.range; + d3_time.hour = d3_time_interval(function(date) { + var timezone = date.getTimezoneOffset() / 60; + return new d3_date((Math.floor(date / 36e5 - timezone) + timezone) * 36e5); + }, function(date, offset) { + date.setTime(date.getTime() + Math.floor(offset) * 36e5); + }, function(date) { + return date.getHours(); + }); + d3_time.hours = d3_time.hour.range; + d3_time.hours.utc = d3_time.hour.utc.range; + d3_time.month = d3_time_interval(function(date) { + date = d3_time.day(date); + date.setDate(1); + return date; + }, function(date, offset) { + date.setMonth(date.getMonth() + offset); + }, function(date) { + return date.getMonth(); + }); + d3_time.months = d3_time.month.range; + d3_time.months.utc = d3_time.month.utc.range; + function d3_time_scale(linear, methods, format) { + function scale(x) { + return linear(x); + } + scale.invert = function(x) { + return d3_time_scaleDate(linear.invert(x)); + }; + scale.domain = function(x) { + if (!arguments.length) return linear.domain().map(d3_time_scaleDate); + linear.domain(x); + return scale; + }; + function tickMethod(extent, count) { + var span = extent[1] - extent[0], target = span / count, i = d3.bisect(d3_time_scaleSteps, target); + return i == d3_time_scaleSteps.length ? [ methods.year, d3_scale_linearTickRange(extent.map(function(d) { + return d / 31536e6; + }), count)[2] ] : !i ? [ d3_time_scaleMilliseconds, d3_scale_linearTickRange(extent, count)[2] ] : methods[target / d3_time_scaleSteps[i - 1] < d3_time_scaleSteps[i] / target ? i - 1 : i]; + } + scale.nice = function(interval, skip) { + var domain = scale.domain(), extent = d3_scaleExtent(domain), method = interval == null ? tickMethod(extent, 10) : typeof interval === "number" && tickMethod(extent, interval); + if (method) interval = method[0], skip = method[1]; + function skipped(date) { + return !isNaN(date) && !interval.range(date, d3_time_scaleDate(+date + 1), skip).length; + } + return scale.domain(d3_scale_nice(domain, skip > 1 ? { + floor: function(date) { + while (skipped(date = interval.floor(date))) date = d3_time_scaleDate(date - 1); + return date; + }, + ceil: function(date) { + while (skipped(date = interval.ceil(date))) date = d3_time_scaleDate(+date + 1); + return date; + } + } : interval)); + }; + scale.ticks = function(interval, skip) { + var extent = d3_scaleExtent(scale.domain()), method = interval == null ? tickMethod(extent, 10) : typeof interval === "number" ? tickMethod(extent, interval) : !interval.range && [ { + range: interval + }, skip ]; + if (method) interval = method[0], skip = method[1]; + return interval.range(extent[0], d3_time_scaleDate(+extent[1] + 1), skip < 1 ? 1 : skip); + }; + scale.tickFormat = function() { + return format; + }; + scale.copy = function() { + return d3_time_scale(linear.copy(), methods, format); + }; + return d3_scale_linearRebind(scale, linear); + } + function d3_time_scaleDate(t) { + return new Date(t); + } + var d3_time_scaleSteps = [ 1e3, 5e3, 15e3, 3e4, 6e4, 3e5, 9e5, 18e5, 36e5, 108e5, 216e5, 432e5, 864e5, 1728e5, 6048e5, 2592e6, 7776e6, 31536e6 ]; + var d3_time_scaleLocalMethods = [ [ d3_time.second, 1 ], [ d3_time.second, 5 ], [ d3_time.second, 15 ], [ d3_time.second, 30 ], [ d3_time.minute, 1 ], [ d3_time.minute, 5 ], [ d3_time.minute, 15 ], [ d3_time.minute, 30 ], [ d3_time.hour, 1 ], [ d3_time.hour, 3 ], [ d3_time.hour, 6 ], [ d3_time.hour, 12 ], [ d3_time.day, 1 ], [ d3_time.day, 2 ], [ d3_time.week, 1 ], [ d3_time.month, 1 ], [ d3_time.month, 3 ], [ d3_time.year, 1 ] ]; + var d3_time_scaleLocalFormat = d3_time_format.multi([ [ ".%L", function(d) { + return d.getMilliseconds(); + } ], [ ":%S", function(d) { + return d.getSeconds(); + } ], [ "%I:%M", function(d) { + return d.getMinutes(); + } ], [ "%I %p", function(d) { + return d.getHours(); + } ], [ "%a %d", function(d) { + return d.getDay() && d.getDate() != 1; + } ], [ "%b %d", function(d) { + return d.getDate() != 1; + } ], [ "%B", function(d) { + return d.getMonth(); + } ], [ "%Y", d3_true ] ]); + var d3_time_scaleMilliseconds = { + range: function(start, stop, step) { + return d3.range(Math.ceil(start / step) * step, +stop, step).map(d3_time_scaleDate); + }, + floor: d3_identity, + ceil: d3_identity + }; + d3_time_scaleLocalMethods.year = d3_time.year; + d3_time.scale = function() { + return d3_time_scale(d3.scale.linear(), d3_time_scaleLocalMethods, d3_time_scaleLocalFormat); + }; + var d3_time_scaleUtcMethods = d3_time_scaleLocalMethods.map(function(m) { + return [ m[0].utc, m[1] ]; + }); + var d3_time_scaleUtcFormat = d3_time_formatUtc.multi([ [ ".%L", function(d) { + return d.getUTCMilliseconds(); + } ], [ ":%S", function(d) { + return d.getUTCSeconds(); + } ], [ "%I:%M", function(d) { + return d.getUTCMinutes(); + } ], [ "%I %p", function(d) { + return d.getUTCHours(); + } ], [ "%a %d", function(d) { + return d.getUTCDay() && d.getUTCDate() != 1; + } ], [ "%b %d", function(d) { + return d.getUTCDate() != 1; + } ], [ "%B", function(d) { + return d.getUTCMonth(); + } ], [ "%Y", d3_true ] ]); + d3_time_scaleUtcMethods.year = d3_time.year.utc; + d3_time.scale.utc = function() { + return d3_time_scale(d3.scale.linear(), d3_time_scaleUtcMethods, d3_time_scaleUtcFormat); + }; + d3.text = d3_xhrType(function(request) { + return request.responseText; + }); + d3.json = function(url, callback) { + return d3_xhr(url, "application/json", d3_json, callback); + }; + function d3_json(request) { + return JSON.parse(request.responseText); + } + d3.html = function(url, callback) { + return d3_xhr(url, "text/html", d3_html, callback); + }; + function d3_html(request) { + var range = d3_document.createRange(); + range.selectNode(d3_document.body); + return range.createContextualFragment(request.responseText); + } + d3.xml = d3_xhrType(function(request) { + return request.responseXML; + }); + if (typeof define === "function" && define.amd) { + define(d3); + } else if (typeof module === "object" && module.exports) { + module.exports = d3; + } else { + this.d3 = d3; + } +}(); \ No newline at end of file diff --git a/vendor/assets/javascripts/expect.js b/vendor/assets/javascripts/expect.js new file mode 100644 index 0000000000..b1e921ddc3 --- /dev/null +++ b/vendor/assets/javascripts/expect.js @@ -0,0 +1,1284 @@ +(function (global, module) { + + var exports = module.exports; + + /** + * Exports. + */ + + module.exports = expect; + expect.Assertion = Assertion; + + /** + * Exports version. + */ + + expect.version = '0.3.1'; + + /** + * Possible assertion flags. + */ + + var flags = { + not: ['to', 'be', 'have', 'include', 'only'] + , to: ['be', 'have', 'include', 'only', 'not'] + , only: ['have'] + , have: ['own'] + , be: ['an'] + }; + + function expect (obj) { + return new Assertion(obj); + } + + /** + * Constructor + * + * @api private + */ + + function Assertion (obj, flag, parent) { + this.obj = obj; + this.flags = {}; + + if (undefined != parent) { + this.flags[flag] = true; + + for (var i in parent.flags) { + if (parent.flags.hasOwnProperty(i)) { + this.flags[i] = true; + } + } + } + + var $flags = flag ? flags[flag] : keys(flags) + , self = this; + + if ($flags) { + for (var i = 0, l = $flags.length; i < l; i++) { + // avoid recursion + if (this.flags[$flags[i]]) continue; + + var name = $flags[i] + , assertion = new Assertion(this.obj, name, this) + + if ('function' == typeof Assertion.prototype[name]) { + // clone the function, make sure we dont touch the prot reference + var old = this[name]; + this[name] = function () { + return old.apply(self, arguments); + }; + + for (var fn in Assertion.prototype) { + if (Assertion.prototype.hasOwnProperty(fn) && fn != name) { + this[name][fn] = bind(assertion[fn], assertion); + } + } + } else { + this[name] = assertion; + } + } + } + } + + /** + * Performs an assertion + * + * @api private + */ + + Assertion.prototype.assert = function (truth, msg, error, expected) { + var msg = this.flags.not ? error : msg + , ok = this.flags.not ? !truth : truth + , err; + + if (!ok) { + err = new Error(msg.call(this)); + if (arguments.length > 3) { + err.actual = this.obj; + err.expected = expected; + err.showDiff = true; + } + throw err; + } + + this.and = new Assertion(this.obj); + }; + + /** + * Check if the value is truthy + * + * @api public + */ + + Assertion.prototype.ok = function () { + this.assert( + !!this.obj + , function(){ return 'expected ' + i(this.obj) + ' to be truthy' } + , function(){ return 'expected ' + i(this.obj) + ' to be falsy' }); + }; + + /** + * Creates an anonymous function which calls fn with arguments. + * + * @api public + */ + + Assertion.prototype.withArgs = function() { + expect(this.obj).to.be.a('function'); + var fn = this.obj; + var args = Array.prototype.slice.call(arguments); + return expect(function() { fn.apply(null, args); }); + }; + + /** + * Assert that the function throws. + * + * @param {Function|RegExp} callback, or regexp to match error string against + * @api public + */ + + Assertion.prototype.throwError = + Assertion.prototype.throwException = function (fn) { + expect(this.obj).to.be.a('function'); + + var thrown = false + , not = this.flags.not; + + try { + this.obj(); + } catch (e) { + if (isRegExp(fn)) { + var subject = 'string' == typeof e ? e : e.message; + if (not) { + expect(subject).to.not.match(fn); + } else { + expect(subject).to.match(fn); + } + } else if ('function' == typeof fn) { + fn(e); + } + thrown = true; + } + + if (isRegExp(fn) && not) { + // in the presence of a matcher, ensure the `not` only applies to + // the matching. + this.flags.not = false; + } + + var name = this.obj.name || 'fn'; + this.assert( + thrown + , function(){ return 'expected ' + name + ' to throw an exception' } + , function(){ return 'expected ' + name + ' not to throw an exception' }); + }; + + /** + * Checks if the array is empty. + * + * @api public + */ + + Assertion.prototype.empty = function () { + var expectation; + + if ('object' == typeof this.obj && null !== this.obj && !isArray(this.obj)) { + if ('number' == typeof this.obj.length) { + expectation = !this.obj.length; + } else { + expectation = !keys(this.obj).length; + } + } else { + if ('string' != typeof this.obj) { + expect(this.obj).to.be.an('object'); + } + + expect(this.obj).to.have.property('length'); + expectation = !this.obj.length; + } + + this.assert( + expectation + , function(){ return 'expected ' + i(this.obj) + ' to be empty' } + , function(){ return 'expected ' + i(this.obj) + ' to not be empty' }); + return this; + }; + + /** + * Checks if the obj exactly equals another. + * + * @api public + */ + + Assertion.prototype.be = + Assertion.prototype.equal = function (obj) { + this.assert( + obj === this.obj + , function(){ return 'expected ' + i(this.obj) + ' to equal ' + i(obj) } + , function(){ return 'expected ' + i(this.obj) + ' to not equal ' + i(obj) }); + return this; + }; + + /** + * Checks if the obj sortof equals another. + * + * @api public + */ + + Assertion.prototype.eql = function (obj) { + this.assert( + expect.eql(this.obj, obj) + , function(){ return 'expected ' + i(this.obj) + ' to sort of equal ' + i(obj) } + , function(){ return 'expected ' + i(this.obj) + ' to sort of not equal ' + i(obj) } + , obj); + return this; + }; + + /** + * Assert within start to finish (inclusive). + * + * @param {Number} start + * @param {Number} finish + * @api public + */ + + Assertion.prototype.within = function (start, finish) { + var range = start + '..' + finish; + this.assert( + this.obj >= start && this.obj <= finish + , function(){ return 'expected ' + i(this.obj) + ' to be within ' + range } + , function(){ return 'expected ' + i(this.obj) + ' to not be within ' + range }); + return this; + }; + + /** + * Assert typeof / instance of + * + * @api public + */ + + Assertion.prototype.a = + Assertion.prototype.an = function (type) { + if ('string' == typeof type) { + // proper english in error msg + var n = /^[aeiou]/.test(type) ? 'n' : ''; + + // typeof with support for 'array' + this.assert( + 'array' == type ? isArray(this.obj) : + 'regexp' == type ? isRegExp(this.obj) : + 'object' == type + ? 'object' == typeof this.obj && null !== this.obj + : type == typeof this.obj + , function(){ return 'expected ' + i(this.obj) + ' to be a' + n + ' ' + type } + , function(){ return 'expected ' + i(this.obj) + ' not to be a' + n + ' ' + type }); + } else { + // instanceof + var name = type.name || 'supplied constructor'; + this.assert( + this.obj instanceof type + , function(){ return 'expected ' + i(this.obj) + ' to be an instance of ' + name } + , function(){ return 'expected ' + i(this.obj) + ' not to be an instance of ' + name }); + } + + return this; + }; + + /** + * Assert numeric value above _n_. + * + * @param {Number} n + * @api public + */ + + Assertion.prototype.greaterThan = + Assertion.prototype.above = function (n) { + this.assert( + this.obj > n + , function(){ return 'expected ' + i(this.obj) + ' to be above ' + n } + , function(){ return 'expected ' + i(this.obj) + ' to be below ' + n }); + return this; + }; + + /** + * Assert numeric value below _n_. + * + * @param {Number} n + * @api public + */ + + Assertion.prototype.lessThan = + Assertion.prototype.below = function (n) { + this.assert( + this.obj < n + , function(){ return 'expected ' + i(this.obj) + ' to be below ' + n } + , function(){ return 'expected ' + i(this.obj) + ' to be above ' + n }); + return this; + }; + + /** + * Assert string value matches _regexp_. + * + * @param {RegExp} regexp + * @api public + */ + + Assertion.prototype.match = function (regexp) { + this.assert( + regexp.exec(this.obj) + , function(){ return 'expected ' + i(this.obj) + ' to match ' + regexp } + , function(){ return 'expected ' + i(this.obj) + ' not to match ' + regexp }); + return this; + }; + + /** + * Assert property "length" exists and has value of _n_. + * + * @param {Number} n + * @api public + */ + + Assertion.prototype.length = function (n) { + expect(this.obj).to.have.property('length'); + var len = this.obj.length; + this.assert( + n == len + , function(){ return 'expected ' + i(this.obj) + ' to have a length of ' + n + ' but got ' + len } + , function(){ return 'expected ' + i(this.obj) + ' to not have a length of ' + len }); + return this; + }; + + /** + * Assert property _name_ exists, with optional _val_. + * + * @param {String} name + * @param {Mixed} val + * @api public + */ + + Assertion.prototype.property = function (name, val) { + if (this.flags.own) { + this.assert( + Object.prototype.hasOwnProperty.call(this.obj, name) + , function(){ return 'expected ' + i(this.obj) + ' to have own property ' + i(name) } + , function(){ return 'expected ' + i(this.obj) + ' to not have own property ' + i(name) }); + return this; + } + + if (this.flags.not && undefined !== val) { + if (undefined === this.obj[name]) { + throw new Error(i(this.obj) + ' has no property ' + i(name)); + } + } else { + var hasProp; + try { + hasProp = name in this.obj + } catch (e) { + hasProp = undefined !== this.obj[name] + } + + this.assert( + hasProp + , function(){ return 'expected ' + i(this.obj) + ' to have a property ' + i(name) } + , function(){ return 'expected ' + i(this.obj) + ' to not have a property ' + i(name) }); + } + + if (undefined !== val) { + this.assert( + val === this.obj[name] + , function(){ return 'expected ' + i(this.obj) + ' to have a property ' + i(name) + + ' of ' + i(val) + ', but got ' + i(this.obj[name]) } + , function(){ return 'expected ' + i(this.obj) + ' to not have a property ' + i(name) + + ' of ' + i(val) }); + } + + this.obj = this.obj[name]; + return this; + }; + + /** + * Assert that the array contains _obj_ or string contains _obj_. + * + * @param {Mixed} obj|string + * @api public + */ + + Assertion.prototype.string = + Assertion.prototype.contain = function (obj) { + if ('string' == typeof this.obj) { + this.assert( + ~this.obj.indexOf(obj) + , function(){ return 'expected ' + i(this.obj) + ' to contain ' + i(obj) } + , function(){ return 'expected ' + i(this.obj) + ' to not contain ' + i(obj) }); + } else { + this.assert( + ~indexOf(this.obj, obj) + , function(){ return 'expected ' + i(this.obj) + ' to contain ' + i(obj) } + , function(){ return 'expected ' + i(this.obj) + ' to not contain ' + i(obj) }); + } + return this; + }; + + /** + * Assert exact keys or inclusion of keys by using + * the `.own` modifier. + * + * @param {Array|String ...} keys + * @api public + */ + + Assertion.prototype.key = + Assertion.prototype.keys = function ($keys) { + var str + , ok = true; + + $keys = isArray($keys) + ? $keys + : Array.prototype.slice.call(arguments); + + if (!$keys.length) throw new Error('keys required'); + + var actual = keys(this.obj) + , len = $keys.length; + + // Inclusion + ok = every($keys, function (key) { + return ~indexOf(actual, key); + }); + + // Strict + if (!this.flags.not && this.flags.only) { + ok = ok && $keys.length == actual.length; + } + + // Key string + if (len > 1) { + $keys = map($keys, function (key) { + return i(key); + }); + var last = $keys.pop(); + str = $keys.join(', ') + ', and ' + last; + } else { + str = i($keys[0]); + } + + // Form + str = (len > 1 ? 'keys ' : 'key ') + str; + + // Have / include + str = (!this.flags.only ? 'include ' : 'only have ') + str; + + // Assertion + this.assert( + ok + , function(){ return 'expected ' + i(this.obj) + ' to ' + str } + , function(){ return 'expected ' + i(this.obj) + ' to not ' + str }); + + return this; + }; + + /** + * Assert a failure. + * + * @param {String ...} custom message + * @api public + */ + Assertion.prototype.fail = function (msg) { + var error = function() { return msg || "explicit failure"; } + this.assert(false, error, error); + return this; + }; + + /** + * Function bind implementation. + */ + + function bind (fn, scope) { + return function () { + return fn.apply(scope, arguments); + } + } + + /** + * Array every compatibility + * + * @see bit.ly/5Fq1N2 + * @api public + */ + + function every (arr, fn, thisObj) { + var scope = thisObj || global; + for (var i = 0, j = arr.length; i < j; ++i) { + if (!fn.call(scope, arr[i], i, arr)) { + return false; + } + } + return true; + } + + /** + * Array indexOf compatibility. + * + * @see bit.ly/a5Dxa2 + * @api public + */ + + function indexOf (arr, o, i) { + if (Array.prototype.indexOf) { + return Array.prototype.indexOf.call(arr, o, i); + } + + if (arr.length === undefined) { + return -1; + } + + for (var j = arr.length, i = i < 0 ? i + j < 0 ? 0 : i + j : i || 0 + ; i < j && arr[i] !== o; i++); + + return j <= i ? -1 : i; + } + + // https://gist.github.com/1044128/ + var getOuterHTML = function(element) { + if ('outerHTML' in element) return element.outerHTML; + var ns = "http://www.w3.org/1999/xhtml"; + var container = document.createElementNS(ns, '_'); + var xmlSerializer = new XMLSerializer(); + var html; + if (document.xmlVersion) { + return xmlSerializer.serializeToString(element); + } else { + container.appendChild(element.cloneNode(false)); + html = container.innerHTML.replace('><', '>' + element.innerHTML + '<'); + container.innerHTML = ''; + return html; + } + }; + + // Returns true if object is a DOM element. + var isDOMElement = function (object) { + if (typeof HTMLElement === 'object') { + return object instanceof HTMLElement; + } else { + return object && + typeof object === 'object' && + object.nodeType === 1 && + typeof object.nodeName === 'string'; + } + }; + + /** + * Inspects an object. + * + * @see taken from node.js `util` module (copyright Joyent, MIT license) + * @api private + */ + + function i (obj, showHidden, depth) { + var seen = []; + + function stylize (str) { + return str; + } + + function format (value, recurseTimes) { + // Provide a hook for user-specified inspect functions. + // Check that value is an object with an inspect function on it + if (value && typeof value.inspect === 'function' && + // Filter out the util module, it's inspect function is special + value !== exports && + // Also filter out any prototype objects using the circular check. + !(value.constructor && value.constructor.prototype === value)) { + return value.inspect(recurseTimes); + } + + // Primitive types cannot have properties + switch (typeof value) { + case 'undefined': + return stylize('undefined', 'undefined'); + + case 'string': + var simple = '\'' + json.stringify(value).replace(/^"|"$/g, '') + .replace(/'/g, "\\'") + .replace(/\\"/g, '"') + '\''; + return stylize(simple, 'string'); + + case 'number': + return stylize('' + value, 'number'); + + case 'boolean': + return stylize('' + value, 'boolean'); + } + // For some reason typeof null is "object", so special case here. + if (value === null) { + return stylize('null', 'null'); + } + + if (isDOMElement(value)) { + return getOuterHTML(value); + } + + // Look up the keys of the object. + var visible_keys = keys(value); + var $keys = showHidden ? Object.getOwnPropertyNames(value) : visible_keys; + + // Functions without properties can be shortcutted. + if (typeof value === 'function' && $keys.length === 0) { + if (isRegExp(value)) { + return stylize('' + value, 'regexp'); + } else { + var name = value.name ? ': ' + value.name : ''; + return stylize('[Function' + name + ']', 'special'); + } + } + + // Dates without properties can be shortcutted + if (isDate(value) && $keys.length === 0) { + return stylize(value.toUTCString(), 'date'); + } + + // Error objects can be shortcutted + if (value instanceof Error) { + return stylize("["+value.toString()+"]", 'Error'); + } + + var base, type, braces; + // Determine the object type + if (isArray(value)) { + type = 'Array'; + braces = ['[', ']']; + } else { + type = 'Object'; + braces = ['{', '}']; + } + + // Make functions say that they are functions + if (typeof value === 'function') { + var n = value.name ? ': ' + value.name : ''; + base = (isRegExp(value)) ? ' ' + value : ' [Function' + n + ']'; + } else { + base = ''; + } + + // Make dates with properties first say the date + if (isDate(value)) { + base = ' ' + value.toUTCString(); + } + + if ($keys.length === 0) { + return braces[0] + base + braces[1]; + } + + if (recurseTimes < 0) { + if (isRegExp(value)) { + return stylize('' + value, 'regexp'); + } else { + return stylize('[Object]', 'special'); + } + } + + seen.push(value); + + var output = map($keys, function (key) { + var name, str; + if (value.__lookupGetter__) { + if (value.__lookupGetter__(key)) { + if (value.__lookupSetter__(key)) { + str = stylize('[Getter/Setter]', 'special'); + } else { + str = stylize('[Getter]', 'special'); + } + } else { + if (value.__lookupSetter__(key)) { + str = stylize('[Setter]', 'special'); + } + } + } + if (indexOf(visible_keys, key) < 0) { + name = '[' + key + ']'; + } + if (!str) { + if (indexOf(seen, value[key]) < 0) { + if (recurseTimes === null) { + str = format(value[key]); + } else { + str = format(value[key], recurseTimes - 1); + } + if (str.indexOf('\n') > -1) { + if (isArray(value)) { + str = map(str.split('\n'), function (line) { + return ' ' + line; + }).join('\n').substr(2); + } else { + str = '\n' + map(str.split('\n'), function (line) { + return ' ' + line; + }).join('\n'); + } + } + } else { + str = stylize('[Circular]', 'special'); + } + } + if (typeof name === 'undefined') { + if (type === 'Array' && key.match(/^\d+$/)) { + return str; + } + name = json.stringify('' + key); + if (name.match(/^"([a-zA-Z_][a-zA-Z_0-9]*)"$/)) { + name = name.substr(1, name.length - 2); + name = stylize(name, 'name'); + } else { + name = name.replace(/'/g, "\\'") + .replace(/\\"/g, '"') + .replace(/(^"|"$)/g, "'"); + name = stylize(name, 'string'); + } + } + + return name + ': ' + str; + }); + + seen.pop(); + + var numLinesEst = 0; + var length = reduce(output, function (prev, cur) { + numLinesEst++; + if (indexOf(cur, '\n') >= 0) numLinesEst++; + return prev + cur.length + 1; + }, 0); + + if (length > 50) { + output = braces[0] + + (base === '' ? '' : base + '\n ') + + ' ' + + output.join(',\n ') + + ' ' + + braces[1]; + + } else { + output = braces[0] + base + ' ' + output.join(', ') + ' ' + braces[1]; + } + + return output; + } + return format(obj, (typeof depth === 'undefined' ? 2 : depth)); + } + + expect.stringify = i; + + function isArray (ar) { + return Object.prototype.toString.call(ar) === '[object Array]'; + } + + function isRegExp(re) { + var s; + try { + s = '' + re; + } catch (e) { + return false; + } + + return re instanceof RegExp || // easy case + // duck-type for context-switching evalcx case + typeof(re) === 'function' && + re.constructor.name === 'RegExp' && + re.compile && + re.test && + re.exec && + s.match(/^\/.*\/[gim]{0,3}$/); + } + + function isDate(d) { + return d instanceof Date; + } + + function keys (obj) { + if (Object.keys) { + return Object.keys(obj); + } + + var keys = []; + + for (var i in obj) { + if (Object.prototype.hasOwnProperty.call(obj, i)) { + keys.push(i); + } + } + + return keys; + } + + function map (arr, mapper, that) { + if (Array.prototype.map) { + return Array.prototype.map.call(arr, mapper, that); + } + + var other= new Array(arr.length); + + for (var i= 0, n = arr.length; i= 2) { + var rv = arguments[1]; + } else { + do { + if (i in this) { + rv = this[i++]; + break; + } + + // if array contains no values, no initial value to return + if (++i >= len) + throw new TypeError(); + } while (true); + } + + for (; i < len; i++) { + if (i in this) + rv = fun.call(null, rv, this[i], i, this); + } + + return rv; + } + + /** + * Asserts deep equality + * + * @see taken from node.js `assert` module (copyright Joyent, MIT license) + * @api private + */ + + expect.eql = function eql(actual, expected) { + // 7.1. All identical values are equivalent, as determined by ===. + if (actual === expected) { + return true; + } else if ('undefined' != typeof Buffer + && Buffer.isBuffer(actual) && Buffer.isBuffer(expected)) { + if (actual.length != expected.length) return false; + + for (var i = 0; i < actual.length; i++) { + if (actual[i] !== expected[i]) return false; + } + + return true; + + // 7.2. If the expected value is a Date object, the actual value is + // equivalent if it is also a Date object that refers to the same time. + } else if (actual instanceof Date && expected instanceof Date) { + return actual.getTime() === expected.getTime(); + + // 7.3. Other pairs that do not both pass typeof value == "object", + // equivalence is determined by ==. + } else if (typeof actual != 'object' && typeof expected != 'object') { + return actual == expected; + // If both are regular expression use the special `regExpEquiv` method + // to determine equivalence. + } else if (isRegExp(actual) && isRegExp(expected)) { + return regExpEquiv(actual, expected); + // 7.4. For all other Object pairs, including Array objects, equivalence is + // determined by having the same number of owned properties (as verified + // with Object.prototype.hasOwnProperty.call), the same set of keys + // (although not necessarily the same order), equivalent values for every + // corresponding key, and an identical "prototype" property. Note: this + // accounts for both named and indexed properties on Arrays. + } else { + return objEquiv(actual, expected); + } + }; + + function isUndefinedOrNull (value) { + return value === null || value === undefined; + } + + function isArguments (object) { + return Object.prototype.toString.call(object) == '[object Arguments]'; + } + + function regExpEquiv (a, b) { + return a.source === b.source && a.global === b.global && + a.ignoreCase === b.ignoreCase && a.multiline === b.multiline; + } + + function objEquiv (a, b) { + if (isUndefinedOrNull(a) || isUndefinedOrNull(b)) + return false; + // an identical "prototype" property. + if (a.prototype !== b.prototype) return false; + //~~~I've managed to break Object.keys through screwy arguments passing. + // Converting to array solves the problem. + if (isArguments(a)) { + if (!isArguments(b)) { + return false; + } + a = pSlice.call(a); + b = pSlice.call(b); + return expect.eql(a, b); + } + try{ + var ka = keys(a), + kb = keys(b), + key, i; + } catch (e) {//happens when one is a string literal and the other isn't + return false; + } + // having the same number of owned properties (keys incorporates hasOwnProperty) + if (ka.length != kb.length) + return false; + //the same set of keys (although not necessarily the same order), + ka.sort(); + kb.sort(); + //~~~cheap key test + for (i = ka.length - 1; i >= 0; i--) { + if (ka[i] != kb[i]) + return false; + } + //equivalent values for every corresponding key, and + //~~~possibly expensive deep test + for (i = ka.length - 1; i >= 0; i--) { + key = ka[i]; + if (!expect.eql(a[key], b[key])) + return false; + } + return true; + } + + var json = (function () { + "use strict"; + + if ('object' == typeof JSON && JSON.parse && JSON.stringify) { + return { + parse: nativeJSON.parse + , stringify: nativeJSON.stringify + } + } + + var JSON = {}; + + function f(n) { + // Format integers to have at least two digits. + return n < 10 ? '0' + n : n; + } + + function date(d, key) { + return isFinite(d.valueOf()) ? + d.getUTCFullYear() + '-' + + f(d.getUTCMonth() + 1) + '-' + + f(d.getUTCDate()) + 'T' + + f(d.getUTCHours()) + ':' + + f(d.getUTCMinutes()) + ':' + + f(d.getUTCSeconds()) + 'Z' : null; + } + + var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, + escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, + gap, + indent, + meta = { // table of character substitutions + '\b': '\\b', + '\t': '\\t', + '\n': '\\n', + '\f': '\\f', + '\r': '\\r', + '"' : '\\"', + '\\': '\\\\' + }, + rep; + + + function quote(string) { + + // If the string contains no control characters, no quote characters, and no + // backslash characters, then we can safely slap some quotes around it. + // Otherwise we must also replace the offending characters with safe escape + // sequences. + + escapable.lastIndex = 0; + return escapable.test(string) ? '"' + string.replace(escapable, function (a) { + var c = meta[a]; + return typeof c === 'string' ? c : + '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4); + }) + '"' : '"' + string + '"'; + } + + + function str(key, holder) { + + // Produce a string from holder[key]. + + var i, // The loop counter. + k, // The member key. + v, // The member value. + length, + mind = gap, + partial, + value = holder[key]; + + // If the value has a toJSON method, call it to obtain a replacement value. + + if (value instanceof Date) { + value = date(key); + } + + // If we were called with a replacer function, then call the replacer to + // obtain a replacement value. + + if (typeof rep === 'function') { + value = rep.call(holder, key, value); + } + + // What happens next depends on the value's type. + + switch (typeof value) { + case 'string': + return quote(value); + + case 'number': + + // JSON numbers must be finite. Encode non-finite numbers as null. + + return isFinite(value) ? String(value) : 'null'; + + case 'boolean': + case 'null': + + // If the value is a boolean or null, convert it to a string. Note: + // typeof null does not produce 'null'. The case is included here in + // the remote chance that this gets fixed someday. + + return String(value); + + // If the type is 'object', we might be dealing with an object or an array or + // null. + + case 'object': + + // Due to a specification blunder in ECMAScript, typeof null is 'object', + // so watch out for that case. + + if (!value) { + return 'null'; + } + + // Make an array to hold the partial results of stringifying this object value. + + gap += indent; + partial = []; + + // Is the value an array? + + if (Object.prototype.toString.apply(value) === '[object Array]') { + + // The value is an array. Stringify every element. Use null as a placeholder + // for non-JSON values. + + length = value.length; + for (i = 0; i < length; i += 1) { + partial[i] = str(i, value) || 'null'; + } + + // Join all of the elements together, separated with commas, and wrap them in + // brackets. + + v = partial.length === 0 ? '[]' : gap ? + '[\n' + gap + partial.join(',\n' + gap) + '\n' + mind + ']' : + '[' + partial.join(',') + ']'; + gap = mind; + return v; + } + + // If the replacer is an array, use it to select the members to be stringified. + + if (rep && typeof rep === 'object') { + length = rep.length; + for (i = 0; i < length; i += 1) { + if (typeof rep[i] === 'string') { + k = rep[i]; + v = str(k, value); + if (v) { + partial.push(quote(k) + (gap ? ': ' : ':') + v); + } + } + } + } else { + + // Otherwise, iterate through all of the keys in the object. + + for (k in value) { + if (Object.prototype.hasOwnProperty.call(value, k)) { + v = str(k, value); + if (v) { + partial.push(quote(k) + (gap ? ': ' : ':') + v); + } + } + } + } + + // Join all of the member texts together, separated with commas, + // and wrap them in braces. + + v = partial.length === 0 ? '{}' : gap ? + '{\n' + gap + partial.join(',\n' + gap) + '\n' + mind + '}' : + '{' + partial.join(',') + '}'; + gap = mind; + return v; + } + } + + // If the JSON object does not yet have a stringify method, give it one. + + JSON.stringify = function (value, replacer, space) { + + // The stringify method takes a value and an optional replacer, and an optional + // space parameter, and returns a JSON text. The replacer can be a function + // that can replace values, or an array of strings that will select the keys. + // A default replacer method can be provided. Use of the space parameter can + // produce text that is more easily readable. + + var i; + gap = ''; + indent = ''; + + // If the space parameter is a number, make an indent string containing that + // many spaces. + + if (typeof space === 'number') { + for (i = 0; i < space; i += 1) { + indent += ' '; + } + + // If the space parameter is a string, it will be used as the indent string. + + } else if (typeof space === 'string') { + indent = space; + } + + // If there is a replacer, it must be a function or an array. + // Otherwise, throw an error. + + rep = replacer; + if (replacer && typeof replacer !== 'function' && + (typeof replacer !== 'object' || + typeof replacer.length !== 'number')) { + throw new Error('JSON.stringify'); + } + + // Make a fake root object containing our value under the key of ''. + // Return the result of stringifying the value. + + return str('', {'': value}); + }; + + // If the JSON object does not yet have a parse method, give it one. + + JSON.parse = function (text, reviver) { + // The parse method takes a text and an optional reviver function, and returns + // a JavaScript value if the text is a valid JSON text. + + var j; + + function walk(holder, key) { + + // The walk method is used to recursively walk the resulting structure so + // that modifications can be made. + + var k, v, value = holder[key]; + if (value && typeof value === 'object') { + for (k in value) { + if (Object.prototype.hasOwnProperty.call(value, k)) { + v = walk(value, k); + if (v !== undefined) { + value[k] = v; + } else { + delete value[k]; + } + } + } + } + return reviver.call(holder, key, value); + } + + + // Parsing happens in four stages. In the first stage, we replace certain + // Unicode characters with escape sequences. JavaScript handles many characters + // incorrectly, either silently deleting them, or treating them as line endings. + + text = String(text); + cx.lastIndex = 0; + if (cx.test(text)) { + text = text.replace(cx, function (a) { + return '\\u' + + ('0000' + a.charCodeAt(0).toString(16)).slice(-4); + }); + } + + // In the second stage, we run the text against regular expressions that look + // for non-JSON patterns. We are especially concerned with '()' and 'new' + // because they can cause invocation, and '=' because it can cause mutation. + // But just to be safe, we want to reject all unexpected forms. + + // We split the second stage into 4 regexp operations in order to work around + // crippling inefficiencies in IE's and Safari's regexp engines. First we + // replace the JSON backslash pairs with '@' (a non-JSON character). Second, we + // replace all simple value tokens with ']' characters. Third, we delete all + // open brackets that follow a colon or comma or that begin the text. Finally, + // we look to see that the remaining characters are only whitespace or ']' or + // ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval. + + if (/^[\],:{}\s]*$/ + .test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@') + .replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']') + .replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) { + + // In the third stage we use the eval function to compile the text into a + // JavaScript structure. The '{' operator is subject to a syntactic ambiguity + // in JavaScript: it can begin a block or an object literal. We wrap the text + // in parens to eliminate the ambiguity. + + j = eval('(' + text + ')'); + + // In the optional fourth stage, we recursively walk the new structure, passing + // each name/value pair to a reviver function for possible transformation. + + return typeof reviver === 'function' ? + walk({'': j}, '') : j; + } + + // If the text is not JSON parseable, then a SyntaxError is thrown. + + throw new SyntaxError('JSON.parse'); + }; + + return JSON; + })(); + + if ('undefined' != typeof window) { + window.expect = module.exports; + } + +})( + this + , 'undefined' != typeof module ? module : {exports: {}} +); diff --git a/vendor/assets/javascripts/goog.js b/vendor/assets/javascripts/goog.js new file mode 100644 index 0000000000..c9f40563fb --- /dev/null +++ b/vendor/assets/javascripts/goog.js @@ -0,0 +1,38 @@ +/** @license + * RequireJS plugin for loading Google Ajax API modules thru `google.load` + * Author: Miller Medeiros + * Version: 0.2.0 (2011/12/06) + * Released under the MIT license + */ +define(['async', 'propertyParser'], function (async, propertyParser) { + + var rParts = /^([^,]+)(?:,([^,]+))?(?:,(.+))?/; + + function parseName(name){ + var match = rParts.exec(name), + data = { + moduleName : match[1], + version : match[2] || '1' + }; + data.settings = propertyParser.parseProperties(match[3]); + return data; + } + + return { + load : function(name, req, onLoad, config){ + if (config.isBuild) { + onLoad(null); //avoid errors on the optimizer + } else { + var data = parseName(name), + settings = data.settings; + + settings.callback = onLoad; + + req(['async!'+ (document.location.protocol === 'https:'? 'https' : 'http') +'://www.google.com/jsapi'], function(){ + google.load(data.moduleName, data.version, settings); + }); + } + } + }; + +}); diff --git a/vendor/assets/javascripts/jasmine.js b/vendor/assets/javascripts/jasmine.js new file mode 100644 index 0000000000..24463ecb83 --- /dev/null +++ b/vendor/assets/javascripts/jasmine.js @@ -0,0 +1,2402 @@ +/* +Copyright (c) 2008-2013 Pivotal Labs + +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. +*/ +function getJasmineRequireObj() { + if (typeof module !== "undefined" && module.exports) { + return exports; + } else { + window.jasmineRequire = window.jasmineRequire || {}; + return window.jasmineRequire; + } +} + +getJasmineRequireObj().core = function(jRequire) { + var j$ = {}; + + jRequire.base(j$); + j$.util = jRequire.util(); + j$.Any = jRequire.Any(); + j$.CallTracker = jRequire.CallTracker(); + j$.Clock = jRequire.Clock(); + j$.DelayedFunctionScheduler = jRequire.DelayedFunctionScheduler(); + j$.Env = jRequire.Env(j$); + j$.ExceptionFormatter = jRequire.ExceptionFormatter(); + j$.Expectation = jRequire.Expectation(); + j$.buildExpectationResult = jRequire.buildExpectationResult(); + j$.JsApiReporter = jRequire.JsApiReporter(); + j$.matchersUtil = jRequire.matchersUtil(j$); + j$.ObjectContaining = jRequire.ObjectContaining(j$); + j$.pp = jRequire.pp(j$); + j$.QueueRunner = jRequire.QueueRunner(); + j$.ReportDispatcher = jRequire.ReportDispatcher(); + j$.Spec = jRequire.Spec(j$); + j$.SpyStrategy = jRequire.SpyStrategy(); + j$.Suite = jRequire.Suite(); + j$.Timer = jRequire.Timer(); + j$.version = jRequire.version(); + + j$.matchers = jRequire.requireMatchers(jRequire, j$); + + return j$; +}; + +getJasmineRequireObj().requireMatchers = function(jRequire, j$) { + var availableMatchers = [ + "toBe", + "toBeCloseTo", + "toBeDefined", + "toBeFalsy", + "toBeGreaterThan", + "toBeLessThan", + "toBeNaN", + "toBeNull", + "toBeTruthy", + "toBeUndefined", + "toContain", + "toEqual", + "toHaveBeenCalled", + "toHaveBeenCalledWith", + "toMatch", + "toThrow", + "toThrowError" + ], + matchers = {}; + + for (var i = 0; i < availableMatchers.length; i++) { + var name = availableMatchers[i]; + matchers[name] = jRequire[name](j$); + } + + return matchers; +}; + +getJasmineRequireObj().base = function(j$) { + j$.unimplementedMethod_ = function() { + throw new Error("unimplemented method"); + }; + + j$.MAX_PRETTY_PRINT_DEPTH = 40; + j$.DEFAULT_TIMEOUT_INTERVAL = 5000; + + j$.getGlobal = (function() { + var jasmineGlobal = eval.call(null, "this"); + return function() { + return jasmineGlobal; + }; + })(); + + j$.getEnv = function(options) { + var env = j$.currentEnv_ = j$.currentEnv_ || new j$.Env(options); + //jasmine. singletons in here (setTimeout blah blah). + return env; + }; + + j$.isArray_ = function(value) { + return j$.isA_("Array", value); + }; + + j$.isString_ = function(value) { + return j$.isA_("String", value); + }; + + j$.isNumber_ = function(value) { + return j$.isA_("Number", value); + }; + + j$.isA_ = function(typeName, value) { + return Object.prototype.toString.apply(value) === '[object ' + typeName + ']'; + }; + + j$.isDomNode = function(obj) { + return obj.nodeType > 0; + }; + + j$.any = function(clazz) { + return new j$.Any(clazz); + }; + + j$.objectContaining = function(sample) { + return new j$.ObjectContaining(sample); + }; + + j$.createSpy = function(name, originalFn) { + + var spyStrategy = new j$.SpyStrategy({ + name: name, + fn: originalFn, + getSpy: function() { return spy; } + }), + callTracker = new j$.CallTracker(), + spy = function() { + callTracker.track({ + object: this, + args: Array.prototype.slice.apply(arguments) + }); + return spyStrategy.exec.apply(this, arguments); + }; + + for (var prop in originalFn) { + if (prop === 'and' || prop === 'calls') { + throw new Error("Jasmine spies would overwrite the 'and' and 'calls' properties on the object being spied upon"); + } + + spy[prop] = originalFn[prop]; + } + + spy.and = spyStrategy; + spy.calls = callTracker; + + return spy; + }; + + j$.isSpy = function(putativeSpy) { + if (!putativeSpy) { + return false; + } + return putativeSpy.and instanceof j$.SpyStrategy && + putativeSpy.calls instanceof j$.CallTracker; + }; + + j$.createSpyObj = function(baseName, methodNames) { + if (!j$.isArray_(methodNames) || methodNames.length === 0) { + throw "createSpyObj requires a non-empty array of method names to create spies for"; + } + var obj = {}; + for (var i = 0; i < methodNames.length; i++) { + obj[methodNames[i]] = j$.createSpy(baseName + '.' + methodNames[i]); + } + return obj; + }; +}; + +getJasmineRequireObj().util = function() { + + var util = {}; + + util.inherit = function(childClass, parentClass) { + var Subclass = function() { + }; + Subclass.prototype = parentClass.prototype; + childClass.prototype = new Subclass(); + }; + + util.htmlEscape = function(str) { + if (!str) { + return str; + } + return str.replace(/&/g, '&') + .replace(//g, '>'); + }; + + util.argsToArray = function(args) { + var arrayOfArgs = []; + for (var i = 0; i < args.length; i++) { + arrayOfArgs.push(args[i]); + } + return arrayOfArgs; + }; + + util.isUndefined = function(obj) { + return obj === void 0; + }; + + return util; +}; + +getJasmineRequireObj().Spec = function(j$) { + function Spec(attrs) { + this.expectationFactory = attrs.expectationFactory; + this.resultCallback = attrs.resultCallback || function() {}; + this.id = attrs.id; + this.description = attrs.description || ''; + this.fn = attrs.fn; + this.beforeFns = attrs.beforeFns || function() { return []; }; + this.afterFns = attrs.afterFns || function() { return []; }; + this.onStart = attrs.onStart || function() {}; + this.exceptionFormatter = attrs.exceptionFormatter || function() {}; + this.getSpecName = attrs.getSpecName || function() { return ''; }; + this.expectationResultFactory = attrs.expectationResultFactory || function() { }; + this.queueRunnerFactory = attrs.queueRunnerFactory || function() {}; + this.catchingExceptions = attrs.catchingExceptions || function() { return true; }; + + this.timer = attrs.timer || {setTimeout: setTimeout, clearTimeout: clearTimeout}; + + if (!this.fn) { + this.pend(); + } + + this.result = { + id: this.id, + description: this.description, + fullName: this.getFullName(), + failedExpectations: [] + }; + } + + Spec.prototype.addExpectationResult = function(passed, data) { + if (passed) { + return; + } + this.result.failedExpectations.push(this.expectationResultFactory(data)); + }; + + Spec.prototype.expect = function(actual) { + return this.expectationFactory(actual, this); + }; + + Spec.prototype.execute = function(onComplete) { + var self = this, + timeout; + + this.onStart(this); + + if (this.markedPending || this.disabled) { + complete(); + return; + } + + function timeoutable(fn) { + return function(done) { + timeout = Function.prototype.apply.apply(self.timer.setTimeout, [j$.getGlobal(), [function() { + onException(new Error('Timeout - Async callback was not invoked within timeout specified by jasmine.DEFAULT_TIMEOUT_INTERVAL.')); + done(); + }, j$.DEFAULT_TIMEOUT_INTERVAL]]); + + var callDone = function() { + clearTimeoutable(); + done(); + }; + + fn.call(this, callDone); //TODO: do we care about more than 1 arg? + }; + } + + function clearTimeoutable() { + Function.prototype.apply.apply(self.timer.clearTimeout, [j$.getGlobal(), [timeout]]); + timeout = void 0; + } + + var allFns = this.beforeFns().concat(this.fn).concat(this.afterFns()), + allTimeoutableFns = []; + for (var i = 0; i < allFns.length; i++) { + var fn = allFns[i]; + allTimeoutableFns.push(fn.length > 0 ? timeoutable(fn) : fn); + } + + this.queueRunnerFactory({ + fns: allTimeoutableFns, + onException: onException, + onComplete: complete + }); + + function onException(e) { + clearTimeoutable(); + if (Spec.isPendingSpecException(e)) { + self.pend(); + return; + } + + self.addExpectationResult(false, { + matcherName: "", + passed: false, + expected: "", + actual: "", + error: e + }); + } + + function complete() { + self.result.status = self.status(); + self.resultCallback(self.result); + + if (onComplete) { + onComplete(); + } + } + }; + + Spec.prototype.disable = function() { + this.disabled = true; + }; + + Spec.prototype.pend = function() { + this.markedPending = true; + }; + + Spec.prototype.status = function() { + if (this.disabled) { + return 'disabled'; + } + + if (this.markedPending) { + return 'pending'; + } + + if (this.result.failedExpectations.length > 0) { + return 'failed'; + } else { + return 'passed'; + } + }; + + Spec.prototype.getFullName = function() { + return this.getSpecName(this); + }; + + Spec.pendingSpecExceptionMessage = "=> marked Pending"; + + Spec.isPendingSpecException = function(e) { + return e.toString().indexOf(Spec.pendingSpecExceptionMessage) !== -1; + }; + + return Spec; +}; + +if (typeof window == void 0 && typeof exports == "object") { + exports.Spec = jasmineRequire.Spec; +} + +getJasmineRequireObj().Env = function(j$) { + function Env(options) { + options = options || {}; + + var self = this; + var global = options.global || j$.getGlobal(); + + var totalSpecsDefined = 0; + + var catchExceptions = true; + + var realSetTimeout = j$.getGlobal().setTimeout; + var realClearTimeout = j$.getGlobal().clearTimeout; + this.clock = new j$.Clock(global, new j$.DelayedFunctionScheduler()); + + var runnableLookupTable = {}; + + var spies = []; + + var currentSpec = null; + var currentSuite = null; + + var reporter = new j$.ReportDispatcher([ + "jasmineStarted", + "jasmineDone", + "suiteStarted", + "suiteDone", + "specStarted", + "specDone" + ]); + + this.specFilter = function() { + return true; + }; + + var equalityTesters = []; + + var customEqualityTesters = []; + this.addCustomEqualityTester = function(tester) { + customEqualityTesters.push(tester); + }; + + j$.Expectation.addCoreMatchers(j$.matchers); + + var nextSpecId = 0; + var getNextSpecId = function() { + return 'spec' + nextSpecId++; + }; + + var nextSuiteId = 0; + var getNextSuiteId = function() { + return 'suite' + nextSuiteId++; + }; + + var expectationFactory = function(actual, spec) { + return j$.Expectation.Factory({ + util: j$.matchersUtil, + customEqualityTesters: customEqualityTesters, + actual: actual, + addExpectationResult: addExpectationResult + }); + + function addExpectationResult(passed, result) { + return spec.addExpectationResult(passed, result); + } + }; + + var specStarted = function(spec) { + currentSpec = spec; + reporter.specStarted(spec.result); + }; + + var beforeFns = function(suite) { + return function() { + var befores = []; + while(suite) { + befores = befores.concat(suite.beforeFns); + suite = suite.parentSuite; + } + return befores.reverse(); + }; + }; + + var afterFns = function(suite) { + return function() { + var afters = []; + while(suite) { + afters = afters.concat(suite.afterFns); + suite = suite.parentSuite; + } + return afters; + }; + }; + + var getSpecName = function(spec, suite) { + return suite.getFullName() + ' ' + spec.description; + }; + + // TODO: we may just be able to pass in the fn instead of wrapping here + var buildExpectationResult = j$.buildExpectationResult, + exceptionFormatter = new j$.ExceptionFormatter(), + expectationResultFactory = function(attrs) { + attrs.messageFormatter = exceptionFormatter.message; + attrs.stackFormatter = exceptionFormatter.stack; + + return buildExpectationResult(attrs); + }; + + // TODO: fix this naming, and here's where the value comes in + this.catchExceptions = function(value) { + catchExceptions = !!value; + return catchExceptions; + }; + + this.catchingExceptions = function() { + return catchExceptions; + }; + + var maximumSpecCallbackDepth = 20; + var currentSpecCallbackDepth = 0; + + function clearStack(fn) { + currentSpecCallbackDepth++; + if (currentSpecCallbackDepth >= maximumSpecCallbackDepth) { + currentSpecCallbackDepth = 0; + realSetTimeout(fn, 0); + } else { + fn(); + } + } + + var catchException = function(e) { + return j$.Spec.isPendingSpecException(e) || catchExceptions; + }; + + var queueRunnerFactory = function(options) { + options.catchException = catchException; + options.clearStack = options.clearStack || clearStack; + + new j$.QueueRunner(options).execute(); + }; + + var topSuite = new j$.Suite({ + env: this, + id: getNextSuiteId(), + description: 'Jasmine__TopLevel__Suite', + queueRunner: queueRunnerFactory, + resultCallback: function() {} // TODO - hook this up + }); + runnableLookupTable[topSuite.id] = topSuite; + currentSuite = topSuite; + + this.topSuite = function() { + return topSuite; + }; + + this.execute = function(runnablesToRun) { + runnablesToRun = runnablesToRun || [topSuite.id]; + + var allFns = []; + for(var i = 0; i < runnablesToRun.length; i++) { + var runnable = runnableLookupTable[runnablesToRun[i]]; + allFns.push((function(runnable) { return function(done) { runnable.execute(done); }; })(runnable)); + } + + reporter.jasmineStarted({ + totalSpecsDefined: totalSpecsDefined + }); + + queueRunnerFactory({fns: allFns, onComplete: reporter.jasmineDone}); + }; + + this.addReporter = function(reporterToAdd) { + reporter.addReporter(reporterToAdd); + }; + + this.addMatchers = function(matchersToAdd) { + j$.Expectation.addMatchers(matchersToAdd); + }; + + this.spyOn = function(obj, methodName) { + if (j$.util.isUndefined(obj)) { + throw new Error("spyOn could not find an object to spy upon for " + methodName + "()"); + } + + if (j$.util.isUndefined(obj[methodName])) { + throw new Error(methodName + '() method does not exist'); + } + + if (obj[methodName] && j$.isSpy(obj[methodName])) { + //TODO?: should this return the current spy? Downside: may cause user confusion about spy state + throw new Error(methodName + ' has already been spied upon'); + } + + var spy = j$.createSpy(methodName, obj[methodName]); + + spies.push({ + spy: spy, + baseObj: obj, + methodName: methodName, + originalValue: obj[methodName] + }); + + obj[methodName] = spy; + + return spy; + }; + + var suiteFactory = function(description) { + var suite = new j$.Suite({ + env: self, + id: getNextSuiteId(), + description: description, + parentSuite: currentSuite, + queueRunner: queueRunnerFactory, + onStart: suiteStarted, + resultCallback: function(attrs) { + reporter.suiteDone(attrs); + } + }); + + runnableLookupTable[suite.id] = suite; + return suite; + }; + + this.describe = function(description, specDefinitions) { + var suite = suiteFactory(description); + + var parentSuite = currentSuite; + parentSuite.addChild(suite); + currentSuite = suite; + + var declarationError = null; + try { + specDefinitions.call(suite); + } catch (e) { + declarationError = e; + } + + if (declarationError) { + this.it("encountered a declaration exception", function() { + throw declarationError; + }); + } + + currentSuite = parentSuite; + + return suite; + }; + + this.xdescribe = function(description, specDefinitions) { + var suite = this.describe(description, specDefinitions); + suite.disable(); + return suite; + }; + + var specFactory = function(description, fn, suite) { + totalSpecsDefined++; + + var spec = new j$.Spec({ + id: getNextSpecId(), + beforeFns: beforeFns(suite), + afterFns: afterFns(suite), + expectationFactory: expectationFactory, + exceptionFormatter: exceptionFormatter, + resultCallback: specResultCallback, + getSpecName: function(spec) { + return getSpecName(spec, suite); + }, + onStart: specStarted, + description: description, + expectationResultFactory: expectationResultFactory, + queueRunnerFactory: queueRunnerFactory, + fn: fn, + timer: {setTimeout: realSetTimeout, clearTimeout: realClearTimeout} + }); + + runnableLookupTable[spec.id] = spec; + + if (!self.specFilter(spec)) { + spec.disable(); + } + + return spec; + + function removeAllSpies() { + for (var i = 0; i < spies.length; i++) { + var spyEntry = spies[i]; + spyEntry.baseObj[spyEntry.methodName] = spyEntry.originalValue; + } + spies = []; + } + + function specResultCallback(result) { + removeAllSpies(); + j$.Expectation.resetMatchers(); + customEqualityTesters = []; + currentSpec = null; + reporter.specDone(result); + } + }; + + var suiteStarted = function(suite) { + reporter.suiteStarted(suite.result); + }; + + this.it = function(description, fn) { + var spec = specFactory(description, fn, currentSuite); + currentSuite.addChild(spec); + return spec; + }; + + this.xit = function(description, fn) { + var spec = this.it(description, fn); + spec.pend(); + return spec; + }; + + this.expect = function(actual) { + return currentSpec.expect(actual); + }; + + this.beforeEach = function(beforeEachFunction) { + currentSuite.beforeEach(beforeEachFunction); + }; + + this.afterEach = function(afterEachFunction) { + currentSuite.afterEach(afterEachFunction); + }; + + this.pending = function() { + throw j$.Spec.pendingSpecExceptionMessage; + }; + } + + return Env; +}; + +getJasmineRequireObj().JsApiReporter = function() { + + var noopTimer = { + start: function(){}, + elapsed: function(){ return 0; } + }; + + function JsApiReporter(options) { + var timer = options.timer || noopTimer, + status = "loaded"; + + this.started = false; + this.finished = false; + + this.jasmineStarted = function() { + this.started = true; + status = 'started'; + timer.start(); + }; + + var executionTime; + + this.jasmineDone = function() { + this.finished = true; + executionTime = timer.elapsed(); + status = 'done'; + }; + + this.status = function() { + return status; + }; + + var suites = {}; + + this.suiteStarted = function(result) { + storeSuite(result); + }; + + this.suiteDone = function(result) { + storeSuite(result); + }; + + function storeSuite(result) { + suites[result.id] = result; + } + + this.suites = function() { + return suites; + }; + + var specs = []; + this.specStarted = function(result) { }; + + this.specDone = function(result) { + specs.push(result); + }; + + this.specResults = function(index, length) { + return specs.slice(index, index + length); + }; + + this.specs = function() { + return specs; + }; + + this.executionTime = function() { + return executionTime; + }; + + } + + return JsApiReporter; +}; + +getJasmineRequireObj().Any = function() { + + function Any(expectedObject) { + this.expectedObject = expectedObject; + } + + Any.prototype.jasmineMatches = function(other) { + if (this.expectedObject == String) { + return typeof other == 'string' || other instanceof String; + } + + if (this.expectedObject == Number) { + return typeof other == 'number' || other instanceof Number; + } + + if (this.expectedObject == Function) { + return typeof other == 'function' || other instanceof Function; + } + + if (this.expectedObject == Object) { + return typeof other == 'object'; + } + + if (this.expectedObject == Boolean) { + return typeof other == 'boolean'; + } + + return other instanceof this.expectedObject; + }; + + Any.prototype.jasmineToString = function() { + return ''; + }; + + return Any; +}; + +getJasmineRequireObj().CallTracker = function() { + + function CallTracker() { + var calls = []; + + this.track = function(context) { + calls.push(context); + }; + + this.any = function() { + return !!calls.length; + }; + + this.count = function() { + return calls.length; + }; + + this.argsFor = function(index) { + var call = calls[index]; + return call ? call.args : []; + }; + + this.all = function() { + return calls; + }; + + this.allArgs = function() { + var callArgs = []; + for(var i = 0; i < calls.length; i++){ + callArgs.push(calls[i].args); + } + + return callArgs; + }; + + this.first = function() { + return calls[0]; + }; + + this.mostRecent = function() { + return calls[calls.length - 1]; + }; + + this.reset = function() { + calls = []; + }; + } + + return CallTracker; +}; + +getJasmineRequireObj().Clock = function() { + function Clock(global, delayedFunctionScheduler) { + var self = this, + realTimingFunctions = { + setTimeout: global.setTimeout, + clearTimeout: global.clearTimeout, + setInterval: global.setInterval, + clearInterval: global.clearInterval + }, + fakeTimingFunctions = { + setTimeout: setTimeout, + clearTimeout: clearTimeout, + setInterval: setInterval, + clearInterval: clearInterval + }, + installed = false, + timer; + + self.install = function() { + replace(global, fakeTimingFunctions); + timer = fakeTimingFunctions; + installed = true; + }; + + self.uninstall = function() { + delayedFunctionScheduler.reset(); + replace(global, realTimingFunctions); + timer = realTimingFunctions; + installed = false; + }; + + self.setTimeout = function(fn, delay, params) { + if (legacyIE()) { + if (arguments.length > 2) { + throw new Error("IE < 9 cannot support extra params to setTimeout without a polyfill"); + } + return timer.setTimeout(fn, delay); + } + return Function.prototype.apply.apply(timer.setTimeout, [global, arguments]); + }; + + self.setInterval = function(fn, delay, params) { + if (legacyIE()) { + if (arguments.length > 2) { + throw new Error("IE < 9 cannot support extra params to setInterval without a polyfill"); + } + return timer.setInterval(fn, delay); + } + return Function.prototype.apply.apply(timer.setInterval, [global, arguments]); + }; + + self.clearTimeout = function(id) { + return Function.prototype.call.apply(timer.clearTimeout, [global, id]); + }; + + self.clearInterval = function(id) { + return Function.prototype.call.apply(timer.clearInterval, [global, id]); + }; + + self.tick = function(millis) { + if (installed) { + delayedFunctionScheduler.tick(millis); + } else { + throw new Error("Mock clock is not installed, use jasmine.clock().install()"); + } + }; + + return self; + + function legacyIE() { + //if these methods are polyfilled, apply will be present + return !(realTimingFunctions.setTimeout || realTimingFunctions.setInterval).apply; + } + + function replace(dest, source) { + for (var prop in source) { + dest[prop] = source[prop]; + } + } + + function setTimeout(fn, delay) { + return delayedFunctionScheduler.scheduleFunction(fn, delay, argSlice(arguments, 2)); + } + + function clearTimeout(id) { + return delayedFunctionScheduler.removeFunctionWithId(id); + } + + function setInterval(fn, interval) { + return delayedFunctionScheduler.scheduleFunction(fn, interval, argSlice(arguments, 2), true); + } + + function clearInterval(id) { + return delayedFunctionScheduler.removeFunctionWithId(id); + } + + function argSlice(argsObj, n) { + return Array.prototype.slice.call(argsObj, 2); + } + } + + return Clock; +}; + +getJasmineRequireObj().DelayedFunctionScheduler = function() { + function DelayedFunctionScheduler() { + var self = this; + var scheduledLookup = []; + var scheduledFunctions = {}; + var currentTime = 0; + var delayedFnCount = 0; + + self.tick = function(millis) { + millis = millis || 0; + var endTime = currentTime + millis; + + runScheduledFunctions(endTime); + currentTime = endTime; + }; + + self.scheduleFunction = function(funcToCall, millis, params, recurring, timeoutKey, runAtMillis) { + var f; + if (typeof(funcToCall) === 'string') { + /* jshint evil: true */ + f = function() { return eval(funcToCall); }; + /* jshint evil: false */ + } else { + f = funcToCall; + } + + millis = millis || 0; + timeoutKey = timeoutKey || ++delayedFnCount; + runAtMillis = runAtMillis || (currentTime + millis); + + var funcToSchedule = { + runAtMillis: runAtMillis, + funcToCall: f, + recurring: recurring, + params: params, + timeoutKey: timeoutKey, + millis: millis + }; + + if (runAtMillis in scheduledFunctions) { + scheduledFunctions[runAtMillis].push(funcToSchedule); + } else { + scheduledFunctions[runAtMillis] = [funcToSchedule]; + scheduledLookup.push(runAtMillis); + scheduledLookup.sort(function (a, b) { + return a - b; + }); + } + + return timeoutKey; + }; + + self.removeFunctionWithId = function(timeoutKey) { + for (var runAtMillis in scheduledFunctions) { + var funcs = scheduledFunctions[runAtMillis]; + var i = indexOfFirstToPass(funcs, function (func) { + return func.timeoutKey === timeoutKey; + }); + + if (i > -1) { + if (funcs.length === 1) { + delete scheduledFunctions[runAtMillis]; + deleteFromLookup(runAtMillis); + } else { + funcs.splice(i, 1); + } + + // intervals get rescheduled when executed, so there's never more + // than a single scheduled function with a given timeoutKey + break; + } + } + }; + + self.reset = function() { + currentTime = 0; + scheduledLookup = []; + scheduledFunctions = {}; + delayedFnCount = 0; + }; + + return self; + + function indexOfFirstToPass(array, testFn) { + var index = -1; + + for (var i = 0; i < array.length; ++i) { + if (testFn(array[i])) { + index = i; + break; + } + } + + return index; + } + + function deleteFromLookup(key) { + var value = Number(key); + var i = indexOfFirstToPass(scheduledLookup, function (millis) { + return millis === value; + }); + + if (i > -1) { + scheduledLookup.splice(i, 1); + } + } + + function reschedule(scheduledFn) { + self.scheduleFunction(scheduledFn.funcToCall, + scheduledFn.millis, + scheduledFn.params, + true, + scheduledFn.timeoutKey, + scheduledFn.runAtMillis + scheduledFn.millis); + } + + function runScheduledFunctions(endTime) { + if (scheduledLookup.length === 0 || scheduledLookup[0] > endTime) { + return; + } + + do { + currentTime = scheduledLookup.shift(); + + var funcsToRun = scheduledFunctions[currentTime]; + delete scheduledFunctions[currentTime]; + + for (var i = 0; i < funcsToRun.length; ++i) { + var funcToRun = funcsToRun[i]; + funcToRun.funcToCall.apply(null, funcToRun.params || []); + + if (funcToRun.recurring) { + reschedule(funcToRun); + } + } + } while (scheduledLookup.length > 0 && + // checking first if we're out of time prevents setTimeout(0) + // scheduled in a funcToRun from forcing an extra iteration + currentTime !== endTime && + scheduledLookup[0] <= endTime); + } + } + + return DelayedFunctionScheduler; +}; + +getJasmineRequireObj().ExceptionFormatter = function() { + function ExceptionFormatter() { + this.message = function(error) { + var message = error.name + + ': ' + + error.message; + + if (error.fileName || error.sourceURL) { + message += " in " + (error.fileName || error.sourceURL); + } + + if (error.line || error.lineNumber) { + message += " (line " + (error.line || error.lineNumber) + ")"; + } + + return message; + }; + + this.stack = function(error) { + return error ? error.stack : null; + }; + } + + return ExceptionFormatter; +}; + +getJasmineRequireObj().Expectation = function() { + + var matchers = {}; + + function Expectation(options) { + this.util = options.util || { buildFailureMessage: function() {} }; + this.customEqualityTesters = options.customEqualityTesters || []; + this.actual = options.actual; + this.addExpectationResult = options.addExpectationResult || function(){}; + this.isNot = options.isNot; + + for (var matcherName in matchers) { + this[matcherName] = matchers[matcherName]; + } + } + + Expectation.prototype.wrapCompare = function(name, matcherFactory) { + return function() { + var args = Array.prototype.slice.call(arguments, 0), + expected = args.slice(0), + message = ""; + + args.unshift(this.actual); + + var matcher = matcherFactory(this.util, this.customEqualityTesters), + matcherCompare = matcher.compare; + + function defaultNegativeCompare() { + var result = matcher.compare.apply(null, args); + result.pass = !result.pass; + return result; + } + + if (this.isNot) { + matcherCompare = matcher.negativeCompare || defaultNegativeCompare; + } + + var result = matcherCompare.apply(null, args); + + if (!result.pass) { + if (!result.message) { + args.unshift(this.isNot); + args.unshift(name); + message = this.util.buildFailureMessage.apply(null, args); + } else { + message = result.message; + } + } + + if (expected.length == 1) { + expected = expected[0]; + } + + // TODO: how many of these params are needed? + this.addExpectationResult( + result.pass, + { + matcherName: name, + passed: result.pass, + message: message, + actual: this.actual, + expected: expected // TODO: this may need to be arrayified/sliced + } + ); + }; + }; + + Expectation.addCoreMatchers = function(matchers) { + var prototype = Expectation.prototype; + for (var matcherName in matchers) { + var matcher = matchers[matcherName]; + prototype[matcherName] = prototype.wrapCompare(matcherName, matcher); + } + }; + + Expectation.addMatchers = function(matchersToAdd) { + for (var name in matchersToAdd) { + var matcher = matchersToAdd[name]; + matchers[name] = Expectation.prototype.wrapCompare(name, matcher); + } + }; + + Expectation.resetMatchers = function() { + for (var name in matchers) { + delete matchers[name]; + } + }; + + Expectation.Factory = function(options) { + options = options || {}; + + var expect = new Expectation(options); + + // TODO: this would be nice as its own Object - NegativeExpectation + // TODO: copy instead of mutate options + options.isNot = true; + expect.not = new Expectation(options); + + return expect; + }; + + return Expectation; +}; + +//TODO: expectation result may make more sense as a presentation of an expectation. +getJasmineRequireObj().buildExpectationResult = function() { + function buildExpectationResult(options) { + var messageFormatter = options.messageFormatter || function() {}, + stackFormatter = options.stackFormatter || function() {}; + + return { + matcherName: options.matcherName, + expected: options.expected, + actual: options.actual, + message: message(), + stack: stack(), + passed: options.passed + }; + + function message() { + if (options.passed) { + return "Passed."; + } else if (options.message) { + return options.message; + } else if (options.error) { + return messageFormatter(options.error); + } + return ""; + } + + function stack() { + if (options.passed) { + return ""; + } + + var error = options.error; + if (!error) { + try { + throw new Error(message()); + } catch (e) { + error = e; + } + } + return stackFormatter(error); + } + } + + return buildExpectationResult; +}; + +getJasmineRequireObj().ObjectContaining = function(j$) { + + function ObjectContaining(sample) { + this.sample = sample; + } + + ObjectContaining.prototype.jasmineMatches = function(other, mismatchKeys, mismatchValues) { + if (typeof(this.sample) !== "object") { throw new Error("You must provide an object to objectContaining, not '"+this.sample+"'."); } + + mismatchKeys = mismatchKeys || []; + mismatchValues = mismatchValues || []; + + var hasKey = function(obj, keyName) { + return obj !== null && !j$.util.isUndefined(obj[keyName]); + }; + + for (var property in this.sample) { + if (!hasKey(other, property) && hasKey(this.sample, property)) { + mismatchKeys.push("expected has key '" + property + "', but missing from actual."); + } + else if (!j$.matchersUtil.equals(this.sample[property], other[property])) { + mismatchValues.push("'" + property + "' was '" + (other[property] ? j$.util.htmlEscape(other[property].toString()) : other[property]) + "' in actual, but was '" + (this.sample[property] ? j$.util.htmlEscape(this.sample[property].toString()) : this.sample[property]) + "' in expected."); + } + } + + return (mismatchKeys.length === 0 && mismatchValues.length === 0); + }; + + ObjectContaining.prototype.jasmineToString = function() { + return ""; + }; + + return ObjectContaining; +}; + +getJasmineRequireObj().pp = function(j$) { + + function PrettyPrinter() { + this.ppNestLevel_ = 0; + } + + PrettyPrinter.prototype.format = function(value) { + this.ppNestLevel_++; + try { + if (j$.util.isUndefined(value)) { + this.emitScalar('undefined'); + } else if (value === null) { + this.emitScalar('null'); + } else if (value === j$.getGlobal()) { + this.emitScalar(''); + } else if (value.jasmineToString) { + this.emitScalar(value.jasmineToString()); + } else if (typeof value === 'string') { + this.emitString(value); + } else if (j$.isSpy(value)) { + this.emitScalar("spy on " + value.and.identity()); + } else if (value instanceof RegExp) { + this.emitScalar(value.toString()); + } else if (typeof value === 'function') { + this.emitScalar('Function'); + } else if (typeof value.nodeType === 'number') { + this.emitScalar('HTMLNode'); + } else if (value instanceof Date) { + this.emitScalar('Date(' + value + ')'); + } else if (value.__Jasmine_been_here_before__) { + this.emitScalar(''); + } else if (j$.isArray_(value) || j$.isA_('Object', value)) { + value.__Jasmine_been_here_before__ = true; + if (j$.isArray_(value)) { + this.emitArray(value); + } else { + this.emitObject(value); + } + delete value.__Jasmine_been_here_before__; + } else { + this.emitScalar(value.toString()); + } + } finally { + this.ppNestLevel_--; + } + }; + + PrettyPrinter.prototype.iterateObject = function(obj, fn) { + for (var property in obj) { + if (!obj.hasOwnProperty(property)) { continue; } + if (property == '__Jasmine_been_here_before__') { continue; } + fn(property, obj.__lookupGetter__ ? (!j$.util.isUndefined(obj.__lookupGetter__(property)) && + obj.__lookupGetter__(property) !== null) : false); + } + }; + + PrettyPrinter.prototype.emitArray = j$.unimplementedMethod_; + PrettyPrinter.prototype.emitObject = j$.unimplementedMethod_; + PrettyPrinter.prototype.emitScalar = j$.unimplementedMethod_; + PrettyPrinter.prototype.emitString = j$.unimplementedMethod_; + + function StringPrettyPrinter() { + PrettyPrinter.call(this); + + this.string = ''; + } + + j$.util.inherit(StringPrettyPrinter, PrettyPrinter); + + StringPrettyPrinter.prototype.emitScalar = function(value) { + this.append(value); + }; + + StringPrettyPrinter.prototype.emitString = function(value) { + this.append("'" + value + "'"); + }; + + StringPrettyPrinter.prototype.emitArray = function(array) { + if (this.ppNestLevel_ > j$.MAX_PRETTY_PRINT_DEPTH) { + this.append("Array"); + return; + } + + this.append('[ '); + for (var i = 0; i < array.length; i++) { + if (i > 0) { + this.append(', '); + } + this.format(array[i]); + } + this.append(' ]'); + }; + + StringPrettyPrinter.prototype.emitObject = function(obj) { + if (this.ppNestLevel_ > j$.MAX_PRETTY_PRINT_DEPTH) { + this.append("Object"); + return; + } + + var self = this; + this.append('{ '); + var first = true; + + this.iterateObject(obj, function(property, isGetter) { + if (first) { + first = false; + } else { + self.append(', '); + } + + self.append(property); + self.append(' : '); + if (isGetter) { + self.append(''); + } else { + self.format(obj[property]); + } + }); + + this.append(' }'); + }; + + StringPrettyPrinter.prototype.append = function(value) { + this.string += value; + }; + + return function(value) { + var stringPrettyPrinter = new StringPrettyPrinter(); + stringPrettyPrinter.format(value); + return stringPrettyPrinter.string; + }; +}; + +getJasmineRequireObj().QueueRunner = function() { + + function QueueRunner(attrs) { + this.fns = attrs.fns || []; + this.onComplete = attrs.onComplete || function() {}; + this.clearStack = attrs.clearStack || function(fn) {fn();}; + this.onException = attrs.onException || function() {}; + this.catchException = attrs.catchException || function() { return true; }; + this.userContext = {}; + } + + QueueRunner.prototype.execute = function() { + this.run(this.fns, 0); + }; + + QueueRunner.prototype.run = function(fns, recursiveIndex) { + var length = fns.length, + self = this, + iterativeIndex; + + for(iterativeIndex = recursiveIndex; iterativeIndex < length; iterativeIndex++) { + var fn = fns[iterativeIndex]; + if (fn.length > 0) { + return attemptAsync(fn); + } else { + attemptSync(fn); + } + } + + var runnerDone = iterativeIndex >= length; + + if (runnerDone) { + this.clearStack(this.onComplete); + } + + function attemptSync(fn) { + try { + fn.call(self.userContext); + } catch (e) { + handleException(e); + } + } + + function attemptAsync(fn) { + var next = function () { self.run(fns, iterativeIndex + 1); }; + + try { + fn.call(self.userContext, next); + } catch (e) { + handleException(e); + next(); + } + } + + function handleException(e) { + self.onException(e); + if (!self.catchException(e)) { + //TODO: set a var when we catch an exception and + //use a finally block to close the loop in a nice way.. + throw e; + } + } + }; + + return QueueRunner; +}; + +getJasmineRequireObj().ReportDispatcher = function() { + function ReportDispatcher(methods) { + + var dispatchedMethods = methods || []; + + for (var i = 0; i < dispatchedMethods.length; i++) { + var method = dispatchedMethods[i]; + this[method] = (function(m) { + return function() { + dispatch(m, arguments); + }; + }(method)); + } + + var reporters = []; + + this.addReporter = function(reporter) { + reporters.push(reporter); + }; + + return this; + + function dispatch(method, args) { + for (var i = 0; i < reporters.length; i++) { + var reporter = reporters[i]; + if (reporter[method]) { + reporter[method].apply(reporter, args); + } + } + } + } + + return ReportDispatcher; +}; + + +getJasmineRequireObj().SpyStrategy = function() { + + function SpyStrategy(options) { + options = options || {}; + + var identity = options.name || "unknown", + originalFn = options.fn || function() {}, + getSpy = options.getSpy || function() {}, + plan = function() {}; + + this.identity = function() { + return identity; + }; + + this.exec = function() { + return plan.apply(this, arguments); + }; + + this.callThrough = function() { + plan = originalFn; + return getSpy(); + }; + + this.returnValue = function(value) { + plan = function() { + return value; + }; + return getSpy(); + }; + + this.throwError = function(something) { + var error = (something instanceof Error) ? something : new Error(something); + plan = function() { + throw error; + }; + return getSpy(); + }; + + this.callFake = function(fn) { + plan = fn; + return getSpy(); + }; + + this.stub = function(fn) { + plan = function() {}; + return getSpy(); + }; + } + + return SpyStrategy; +}; + +getJasmineRequireObj().Suite = function() { + function Suite(attrs) { + this.env = attrs.env; + this.id = attrs.id; + this.parentSuite = attrs.parentSuite; + this.description = attrs.description; + this.onStart = attrs.onStart || function() {}; + this.resultCallback = attrs.resultCallback || function() {}; + this.clearStack = attrs.clearStack || function(fn) {fn();}; + + this.beforeFns = []; + this.afterFns = []; + this.queueRunner = attrs.queueRunner || function() {}; + this.disabled = false; + + this.children = []; + + this.result = { + id: this.id, + status: this.disabled ? 'disabled' : '', + description: this.description, + fullName: this.getFullName() + }; + } + + Suite.prototype.getFullName = function() { + var fullName = this.description; + for (var parentSuite = this.parentSuite; parentSuite; parentSuite = parentSuite.parentSuite) { + if (parentSuite.parentSuite) { + fullName = parentSuite.description + ' ' + fullName; + } + } + return fullName; + }; + + Suite.prototype.disable = function() { + this.disabled = true; + }; + + Suite.prototype.beforeEach = function(fn) { + this.beforeFns.unshift(fn); + }; + + Suite.prototype.afterEach = function(fn) { + this.afterFns.unshift(fn); + }; + + Suite.prototype.addChild = function(child) { + this.children.push(child); + }; + + Suite.prototype.execute = function(onComplete) { + var self = this; + if (this.disabled) { + complete(); + return; + } + + var allFns = []; + + for (var i = 0; i < this.children.length; i++) { + allFns.push(wrapChildAsAsync(this.children[i])); + } + + this.onStart(this); + + this.queueRunner({ + fns: allFns, + onComplete: complete + }); + + function complete() { + self.resultCallback(self.result); + + if (onComplete) { + onComplete(); + } + } + + function wrapChildAsAsync(child) { + return function(done) { child.execute(done); }; + } + }; + + return Suite; +}; + +if (typeof window == void 0 && typeof exports == "object") { + exports.Suite = jasmineRequire.Suite; +} + +getJasmineRequireObj().Timer = function() { + function Timer(options) { + options = options || {}; + + var now = options.now || function() { return new Date().getTime(); }, + startTime; + + this.start = function() { + startTime = now(); + }; + + this.elapsed = function() { + return now() - startTime; + }; + } + + return Timer; +}; + +getJasmineRequireObj().matchersUtil = function(j$) { + // TODO: what to do about jasmine.pp not being inject? move to JSON.stringify? gut PrettyPrinter? + + return { + equals: function(a, b, customTesters) { + customTesters = customTesters || []; + + return eq(a, b, [], [], customTesters); + }, + + contains: function(haystack, needle, customTesters) { + customTesters = customTesters || []; + + if (Object.prototype.toString.apply(haystack) === "[object Array]") { + for (var i = 0; i < haystack.length; i++) { + if (eq(haystack[i], needle, [], [], customTesters)) { + return true; + } + } + return false; + } + return haystack.indexOf(needle) >= 0; + }, + + buildFailureMessage: function() { + var args = Array.prototype.slice.call(arguments, 0), + matcherName = args[0], + isNot = args[1], + actual = args[2], + expected = args.slice(3), + englishyPredicate = matcherName.replace(/[A-Z]/g, function(s) { return ' ' + s.toLowerCase(); }); + + var message = "Expected " + + j$.pp(actual) + + (isNot ? " not " : " ") + + englishyPredicate; + + if (expected.length > 0) { + for (var i = 0; i < expected.length; i++) { + if (i > 0) { + message += ","; + } + message += " " + j$.pp(expected[i]); + } + } + + return message + "."; + } + }; + + // Equality function lovingly adapted from isEqual in + // [Underscore](http://underscorejs.org) + function eq(a, b, aStack, bStack, customTesters) { + var result = true; + + for (var i = 0; i < customTesters.length; i++) { + var customTesterResult = customTesters[i](a, b); + if (!j$.util.isUndefined(customTesterResult)) { + return customTesterResult; + } + } + + if (a instanceof j$.Any) { + result = a.jasmineMatches(b); + if (result) { + return true; + } + } + + if (b instanceof j$.Any) { + result = b.jasmineMatches(a); + if (result) { + return true; + } + } + + if (b instanceof j$.ObjectContaining) { + result = b.jasmineMatches(a); + if (result) { + return true; + } + } + + if (a instanceof Error && b instanceof Error) { + return a.message == b.message; + } + + // Identical objects are equal. `0 === -0`, but they aren't identical. + // See the [Harmony `egal` proposal](http://wiki.ecmascript.org/doku.php?id=harmony:egal). + if (a === b) { return a !== 0 || 1 / a == 1 / b; } + // A strict comparison is necessary because `null == undefined`. + if (a === null || b === null) { return a === b; } + var className = Object.prototype.toString.call(a); + if (className != Object.prototype.toString.call(b)) { return false; } + switch (className) { + // Strings, numbers, dates, and booleans are compared by value. + case '[object String]': + // Primitives and their corresponding object wrappers are equivalent; thus, `"5"` is + // equivalent to `new String("5")`. + return a == String(b); + case '[object Number]': + // `NaN`s are equivalent, but non-reflexive. An `egal` comparison is performed for + // other numeric values. + return a != +a ? b != +b : (a === 0 ? 1 / a == 1 / b : a == +b); + case '[object Date]': + case '[object Boolean]': + // Coerce dates and booleans to numeric primitive values. Dates are compared by their + // millisecond representations. Note that invalid dates with millisecond representations + // of `NaN` are not equivalent. + return +a == +b; + // RegExps are compared by their source patterns and flags. + case '[object RegExp]': + return a.source == b.source && + a.global == b.global && + a.multiline == b.multiline && + a.ignoreCase == b.ignoreCase; + } + if (typeof a != 'object' || typeof b != 'object') { return false; } + // Assume equality for cyclic structures. The algorithm for detecting cyclic + // structures is adapted from ES 5.1 section 15.12.3, abstract operation `JO`. + var length = aStack.length; + while (length--) { + // Linear search. Performance is inversely proportional to the number of + // unique nested structures. + if (aStack[length] == a) { return bStack[length] == b; } + } + // Add the first object to the stack of traversed objects. + aStack.push(a); + bStack.push(b); + var size = 0; + // Recursively compare objects and arrays. + if (className == '[object Array]') { + // Compare array lengths to determine if a deep comparison is necessary. + size = a.length; + result = size == b.length; + if (result) { + // Deep compare the contents, ignoring non-numeric properties. + while (size--) { + if (!(result = eq(a[size], b[size], aStack, bStack, customTesters))) { break; } + } + } + } else { + // Objects with different constructors are not equivalent, but `Object`s + // from different frames are. + var aCtor = a.constructor, bCtor = b.constructor; + if (aCtor !== bCtor && !(isFunction(aCtor) && (aCtor instanceof aCtor) && + isFunction(bCtor) && (bCtor instanceof bCtor))) { + return false; + } + // Deep compare objects. + for (var key in a) { + if (has(a, key)) { + // Count the expected number of properties. + size++; + // Deep compare each member. + if (!(result = has(b, key) && eq(a[key], b[key], aStack, bStack, customTesters))) { break; } + } + } + // Ensure that both objects contain the same number of properties. + if (result) { + for (key in b) { + if (has(b, key) && !(size--)) { break; } + } + result = !size; + } + } + // Remove the first object from the stack of traversed objects. + aStack.pop(); + bStack.pop(); + + return result; + + function has(obj, key) { + return obj.hasOwnProperty(key); + } + + function isFunction(obj) { + return typeof obj === 'function'; + } + } +}; + +getJasmineRequireObj().toBe = function() { + function toBe() { + return { + compare: function(actual, expected) { + return { + pass: actual === expected + }; + } + }; + } + + return toBe; +}; + +getJasmineRequireObj().toBeCloseTo = function() { + + function toBeCloseTo() { + return { + compare: function(actual, expected, precision) { + if (precision !== 0) { + precision = precision || 2; + } + + return { + pass: Math.abs(expected - actual) < (Math.pow(10, -precision) / 2) + }; + } + }; + } + + return toBeCloseTo; +}; + +getJasmineRequireObj().toBeDefined = function() { + function toBeDefined() { + return { + compare: function(actual) { + return { + pass: (void 0 !== actual) + }; + } + }; + } + + return toBeDefined; +}; + +getJasmineRequireObj().toBeFalsy = function() { + function toBeFalsy() { + return { + compare: function(actual) { + return { + pass: !!!actual + }; + } + }; + } + + return toBeFalsy; +}; + +getJasmineRequireObj().toBeGreaterThan = function() { + + function toBeGreaterThan() { + return { + compare: function(actual, expected) { + return { + pass: actual > expected + }; + } + }; + } + + return toBeGreaterThan; +}; + + +getJasmineRequireObj().toBeLessThan = function() { + function toBeLessThan() { + return { + + compare: function(actual, expected) { + return { + pass: actual < expected + }; + } + }; + } + + return toBeLessThan; +}; +getJasmineRequireObj().toBeNaN = function(j$) { + + function toBeNaN() { + return { + compare: function(actual) { + var result = { + pass: (actual !== actual) + }; + + if (result.pass) { + result.message = "Expected actual not to be NaN."; + } else { + result.message = "Expected " + j$.pp(actual) + " to be NaN."; + } + + return result; + } + }; + } + + return toBeNaN; +}; + +getJasmineRequireObj().toBeNull = function() { + + function toBeNull() { + return { + compare: function(actual) { + return { + pass: actual === null + }; + } + }; + } + + return toBeNull; +}; + +getJasmineRequireObj().toBeTruthy = function() { + + function toBeTruthy() { + return { + compare: function(actual) { + return { + pass: !!actual + }; + } + }; + } + + return toBeTruthy; +}; + +getJasmineRequireObj().toBeUndefined = function() { + + function toBeUndefined() { + return { + compare: function(actual) { + return { + pass: void 0 === actual + }; + } + }; + } + + return toBeUndefined; +}; + +getJasmineRequireObj().toContain = function() { + function toContain(util, customEqualityTesters) { + customEqualityTesters = customEqualityTesters || []; + + return { + compare: function(actual, expected) { + + return { + pass: util.contains(actual, expected, customEqualityTesters) + }; + } + }; + } + + return toContain; +}; + +getJasmineRequireObj().toEqual = function() { + + function toEqual(util, customEqualityTesters) { + customEqualityTesters = customEqualityTesters || []; + + return { + compare: function(actual, expected) { + var result = { + pass: false + }; + + result.pass = util.equals(actual, expected, customEqualityTesters); + + return result; + } + }; + } + + return toEqual; +}; + +getJasmineRequireObj().toHaveBeenCalled = function(j$) { + + function toHaveBeenCalled() { + return { + compare: function(actual) { + var result = {}; + + if (!j$.isSpy(actual)) { + throw new Error('Expected a spy, but got ' + j$.pp(actual) + '.'); + } + + if (arguments.length > 1) { + throw new Error('toHaveBeenCalled does not take arguments, use toHaveBeenCalledWith'); + } + + result.pass = actual.calls.any(); + + result.message = result.pass ? + "Expected spy " + actual.and.identity() + " not to have been called." : + "Expected spy " + actual.and.identity() + " to have been called."; + + return result; + } + }; + } + + return toHaveBeenCalled; +}; + +getJasmineRequireObj().toHaveBeenCalledWith = function(j$) { + + function toHaveBeenCalledWith(util) { + return { + compare: function() { + var args = Array.prototype.slice.call(arguments, 0), + actual = args[0], + expectedArgs = args.slice(1), + result = { pass: false }; + + if (!j$.isSpy(actual)) { + throw new Error('Expected a spy, but got ' + j$.pp(actual) + '.'); + } + + if (!actual.calls.any()) { + result.message = "Expected spy " + actual.and.identity() + " to have been called with " + j$.pp(expectedArgs) + " but it was never called."; + return result; + } + + if (util.contains(actual.calls.allArgs(), expectedArgs)) { + result.pass = true; + result.message = "Expected spy " + actual.and.identity() + " not to have been called with " + j$.pp(expectedArgs) + " but it was."; + } else { + result.message = "Expected spy " + actual.and.identity() + " to have been called with " + j$.pp(expectedArgs) + " but actual calls were " + j$.pp(actual.calls.allArgs()).replace(/^\[ | \]$/g, '') + "."; + } + + return result; + } + }; + } + + return toHaveBeenCalledWith; +}; + +getJasmineRequireObj().toMatch = function() { + + function toMatch() { + return { + compare: function(actual, expected) { + var regexp = new RegExp(expected); + + return { + pass: regexp.test(actual) + }; + } + }; + } + + return toMatch; +}; + +getJasmineRequireObj().toThrow = function(j$) { + + function toThrow(util) { + return { + compare: function(actual, expected) { + var result = { pass: false }, + threw = false, + thrown; + + if (typeof actual != "function") { + throw new Error("Actual is not a Function"); + } + + try { + actual(); + } catch (e) { + threw = true; + thrown = e; + } + + if (!threw) { + result.message = "Expected function to throw an exception."; + return result; + } + + if (arguments.length == 1) { + result.pass = true; + result.message = "Expected function not to throw, but it threw " + j$.pp(thrown) + "."; + + return result; + } + + if (util.equals(thrown, expected)) { + result.pass = true; + result.message = "Expected function not to throw " + j$.pp(expected) + "."; + } else { + result.message = "Expected function to throw " + j$.pp(expected) + ", but it threw " + j$.pp(thrown) + "."; + } + + return result; + } + }; + } + + return toThrow; +}; + +getJasmineRequireObj().toThrowError = function(j$) { + function toThrowError (util) { + return { + compare: function(actual) { + var threw = false, + thrown, + errorType, + message, + regexp, + name, + constructorName; + + if (typeof actual != "function") { + throw new Error("Actual is not a Function"); + } + + extractExpectedParams.apply(null, arguments); + + try { + actual(); + } catch (e) { + threw = true; + thrown = e; + } + + if (!threw) { + return fail("Expected function to throw an Error."); + } + + if (!(thrown instanceof Error)) { + return fail("Expected function to throw an Error, but it threw " + thrown + "."); + } + + if (arguments.length == 1) { + return pass("Expected function not to throw an Error, but it threw " + fnNameFor(thrown) + "."); + } + + if (errorType) { + name = fnNameFor(errorType); + constructorName = fnNameFor(thrown.constructor); + } + + if (errorType && message) { + if (thrown.constructor == errorType && util.equals(thrown.message, message)) { + return pass("Expected function not to throw " + name + " with message \"" + message + "\"."); + } else { + return fail("Expected function to throw " + name + " with message \"" + message + + "\", but it threw " + constructorName + " with message \"" + thrown.message + "\"."); + } + } + + if (errorType && regexp) { + if (thrown.constructor == errorType && regexp.test(thrown.message)) { + return pass("Expected function not to throw " + name + " with message matching " + regexp + "."); + } else { + return fail("Expected function to throw " + name + " with message matching " + regexp + + ", but it threw " + constructorName + " with message \"" + thrown.message + "\"."); + } + } + + if (errorType) { + if (thrown.constructor == errorType) { + return pass("Expected function not to throw " + name + "."); + } else { + return fail("Expected function to throw " + name + ", but it threw " + constructorName + "."); + } + } + + if (message) { + if (thrown.message == message) { + return pass("Expected function not to throw an exception with message " + j$.pp(message) + "."); + } else { + return fail("Expected function to throw an exception with message " + j$.pp(message) + + ", but it threw an exception with message " + j$.pp(thrown.message) + "."); + } + } + + if (regexp) { + if (regexp.test(thrown.message)) { + return pass("Expected function not to throw an exception with a message matching " + j$.pp(regexp) + "."); + } else { + return fail("Expected function to throw an exception with a message matching " + j$.pp(regexp) + + ", but it threw an exception with message " + j$.pp(thrown.message) + "."); + } + } + + function fnNameFor(func) { + return func.name || func.toString().match(/^\s*function\s*(\w*)\s*\(/)[1]; + } + + function pass(notMessage) { + return { + pass: true, + message: notMessage + }; + } + + function fail(message) { + return { + pass: false, + message: message + }; + } + + function extractExpectedParams() { + if (arguments.length == 1) { + return; + } + + if (arguments.length == 2) { + var expected = arguments[1]; + + if (expected instanceof RegExp) { + regexp = expected; + } else if (typeof expected == "string") { + message = expected; + } else if (checkForAnErrorType(expected)) { + errorType = expected; + } + + if (!(errorType || message || regexp)) { + throw new Error("Expected is not an Error, string, or RegExp."); + } + } else { + if (checkForAnErrorType(arguments[1])) { + errorType = arguments[1]; + } else { + throw new Error("Expected error type is not an Error."); + } + + if (arguments[2] instanceof RegExp) { + regexp = arguments[2]; + } else if (typeof arguments[2] == "string") { + message = arguments[2]; + } else { + throw new Error("Expected error message is not a string or RegExp."); + } + } + } + + function checkForAnErrorType(type) { + if (typeof type !== "function") { + return false; + } + + var Surrogate = function() {}; + Surrogate.prototype = type.prototype; + return (new Surrogate()) instanceof Error; + } + } + }; + } + + return toThrowError; +}; + +getJasmineRequireObj().version = function() { + return "2.0.0"; +}; diff --git a/vendor/assets/javascripts/moment.js b/vendor/assets/javascripts/moment.js new file mode 100644 index 0000000000..9c1f3ade9f --- /dev/null +++ b/vendor/assets/javascripts/moment.js @@ -0,0 +1,6 @@ +//! moment.js +//! version : 2.7.0 +//! authors : Tim Wood, Iskren Chernev, Moment.js contributors +//! license : MIT +//! momentjs.com +(function(a){function b(a,b,c){switch(arguments.length){case 2:return null!=a?a:b;case 3:return null!=a?a:null!=b?b:c;default:throw new Error("Implement me")}}function c(){return{empty:!1,unusedTokens:[],unusedInput:[],overflow:-2,charsLeftOver:0,nullInput:!1,invalidMonth:null,invalidFormat:!1,userInvalidated:!1,iso:!1}}function d(a,b){function c(){mb.suppressDeprecationWarnings===!1&&"undefined"!=typeof console&&console.warn&&console.warn("Deprecation warning: "+a)}var d=!0;return j(function(){return d&&(c(),d=!1),b.apply(this,arguments)},b)}function e(a,b){return function(c){return m(a.call(this,c),b)}}function f(a,b){return function(c){return this.lang().ordinal(a.call(this,c),b)}}function g(){}function h(a){z(a),j(this,a)}function i(a){var b=s(a),c=b.year||0,d=b.quarter||0,e=b.month||0,f=b.week||0,g=b.day||0,h=b.hour||0,i=b.minute||0,j=b.second||0,k=b.millisecond||0;this._milliseconds=+k+1e3*j+6e4*i+36e5*h,this._days=+g+7*f,this._months=+e+3*d+12*c,this._data={},this._bubble()}function j(a,b){for(var c in b)b.hasOwnProperty(c)&&(a[c]=b[c]);return b.hasOwnProperty("toString")&&(a.toString=b.toString),b.hasOwnProperty("valueOf")&&(a.valueOf=b.valueOf),a}function k(a){var b,c={};for(b in a)a.hasOwnProperty(b)&&Ab.hasOwnProperty(b)&&(c[b]=a[b]);return c}function l(a){return 0>a?Math.ceil(a):Math.floor(a)}function m(a,b,c){for(var d=""+Math.abs(a),e=a>=0;d.lengthd;d++)(c&&a[d]!==b[d]||!c&&u(a[d])!==u(b[d]))&&g++;return g+f}function r(a){if(a){var b=a.toLowerCase().replace(/(.)s$/,"$1");a=bc[a]||cc[b]||b}return a}function s(a){var b,c,d={};for(c in a)a.hasOwnProperty(c)&&(b=r(c),b&&(d[b]=a[c]));return d}function t(b){var c,d;if(0===b.indexOf("week"))c=7,d="day";else{if(0!==b.indexOf("month"))return;c=12,d="month"}mb[b]=function(e,f){var g,h,i=mb.fn._lang[b],j=[];if("number"==typeof e&&(f=e,e=a),h=function(a){var b=mb().utc().set(d,a);return i.call(mb.fn._lang,b,e||"")},null!=f)return h(f);for(g=0;c>g;g++)j.push(h(g));return j}}function u(a){var b=+a,c=0;return 0!==b&&isFinite(b)&&(c=b>=0?Math.floor(b):Math.ceil(b)),c}function v(a,b){return new Date(Date.UTC(a,b+1,0)).getUTCDate()}function w(a,b,c){return bb(mb([a,11,31+b-c]),b,c).week}function x(a){return y(a)?366:365}function y(a){return a%4===0&&a%100!==0||a%400===0}function z(a){var b;a._a&&-2===a._pf.overflow&&(b=a._a[tb]<0||a._a[tb]>11?tb:a._a[ub]<1||a._a[ub]>v(a._a[sb],a._a[tb])?ub:a._a[vb]<0||a._a[vb]>23?vb:a._a[wb]<0||a._a[wb]>59?wb:a._a[xb]<0||a._a[xb]>59?xb:a._a[yb]<0||a._a[yb]>999?yb:-1,a._pf._overflowDayOfYear&&(sb>b||b>ub)&&(b=ub),a._pf.overflow=b)}function A(a){return null==a._isValid&&(a._isValid=!isNaN(a._d.getTime())&&a._pf.overflow<0&&!a._pf.empty&&!a._pf.invalidMonth&&!a._pf.nullInput&&!a._pf.invalidFormat&&!a._pf.userInvalidated,a._strict&&(a._isValid=a._isValid&&0===a._pf.charsLeftOver&&0===a._pf.unusedTokens.length)),a._isValid}function B(a){return a?a.toLowerCase().replace("_","-"):a}function C(a,b){return b._isUTC?mb(a).zone(b._offset||0):mb(a).local()}function D(a,b){return b.abbr=a,zb[a]||(zb[a]=new g),zb[a].set(b),zb[a]}function E(a){delete zb[a]}function F(a){var b,c,d,e,f=0,g=function(a){if(!zb[a]&&Bb)try{require("./lang/"+a)}catch(b){}return zb[a]};if(!a)return mb.fn._lang;if(!o(a)){if(c=g(a))return c;a=[a]}for(;f0;){if(c=g(e.slice(0,b).join("-")))return c;if(d&&d.length>=b&&q(e,d,!0)>=b-1)break;b--}f++}return mb.fn._lang}function G(a){return a.match(/\[[\s\S]/)?a.replace(/^\[|\]$/g,""):a.replace(/\\/g,"")}function H(a){var b,c,d=a.match(Fb);for(b=0,c=d.length;c>b;b++)d[b]=hc[d[b]]?hc[d[b]]:G(d[b]);return function(e){var f="";for(b=0;c>b;b++)f+=d[b]instanceof Function?d[b].call(e,a):d[b];return f}}function I(a,b){return a.isValid()?(b=J(b,a.lang()),dc[b]||(dc[b]=H(b)),dc[b](a)):a.lang().invalidDate()}function J(a,b){function c(a){return b.longDateFormat(a)||a}var d=5;for(Gb.lastIndex=0;d>=0&&Gb.test(a);)a=a.replace(Gb,c),Gb.lastIndex=0,d-=1;return a}function K(a,b){var c,d=b._strict;switch(a){case"Q":return Rb;case"DDDD":return Tb;case"YYYY":case"GGGG":case"gggg":return d?Ub:Jb;case"Y":case"G":case"g":return Wb;case"YYYYYY":case"YYYYY":case"GGGGG":case"ggggg":return d?Vb:Kb;case"S":if(d)return Rb;case"SS":if(d)return Sb;case"SSS":if(d)return Tb;case"DDD":return Ib;case"MMM":case"MMMM":case"dd":case"ddd":case"dddd":return Mb;case"a":case"A":return F(b._l)._meridiemParse;case"X":return Pb;case"Z":case"ZZ":return Nb;case"T":return Ob;case"SSSS":return Lb;case"MM":case"DD":case"YY":case"GG":case"gg":case"HH":case"hh":case"mm":case"ss":case"ww":case"WW":return d?Sb:Hb;case"M":case"D":case"d":case"H":case"h":case"m":case"s":case"w":case"W":case"e":case"E":return Hb;case"Do":return Qb;default:return c=new RegExp(T(S(a.replace("\\","")),"i"))}}function L(a){a=a||"";var b=a.match(Nb)||[],c=b[b.length-1]||[],d=(c+"").match(_b)||["-",0,0],e=+(60*d[1])+u(d[2]);return"+"===d[0]?-e:e}function M(a,b,c){var d,e=c._a;switch(a){case"Q":null!=b&&(e[tb]=3*(u(b)-1));break;case"M":case"MM":null!=b&&(e[tb]=u(b)-1);break;case"MMM":case"MMMM":d=F(c._l).monthsParse(b),null!=d?e[tb]=d:c._pf.invalidMonth=b;break;case"D":case"DD":null!=b&&(e[ub]=u(b));break;case"Do":null!=b&&(e[ub]=u(parseInt(b,10)));break;case"DDD":case"DDDD":null!=b&&(c._dayOfYear=u(b));break;case"YY":e[sb]=mb.parseTwoDigitYear(b);break;case"YYYY":case"YYYYY":case"YYYYYY":e[sb]=u(b);break;case"a":case"A":c._isPm=F(c._l).isPM(b);break;case"H":case"HH":case"h":case"hh":e[vb]=u(b);break;case"m":case"mm":e[wb]=u(b);break;case"s":case"ss":e[xb]=u(b);break;case"S":case"SS":case"SSS":case"SSSS":e[yb]=u(1e3*("0."+b));break;case"X":c._d=new Date(1e3*parseFloat(b));break;case"Z":case"ZZ":c._useUTC=!0,c._tzm=L(b);break;case"dd":case"ddd":case"dddd":d=F(c._l).weekdaysParse(b),null!=d?(c._w=c._w||{},c._w.d=d):c._pf.invalidWeekday=b;break;case"w":case"ww":case"W":case"WW":case"d":case"e":case"E":a=a.substr(0,1);case"gggg":case"GGGG":case"GGGGG":a=a.substr(0,2),b&&(c._w=c._w||{},c._w[a]=u(b));break;case"gg":case"GG":c._w=c._w||{},c._w[a]=mb.parseTwoDigitYear(b)}}function N(a){var c,d,e,f,g,h,i,j;c=a._w,null!=c.GG||null!=c.W||null!=c.E?(g=1,h=4,d=b(c.GG,a._a[sb],bb(mb(),1,4).year),e=b(c.W,1),f=b(c.E,1)):(j=F(a._l),g=j._week.dow,h=j._week.doy,d=b(c.gg,a._a[sb],bb(mb(),g,h).year),e=b(c.w,1),null!=c.d?(f=c.d,g>f&&++e):f=null!=c.e?c.e+g:g),i=cb(d,e,f,h,g),a._a[sb]=i.year,a._dayOfYear=i.dayOfYear}function O(a){var c,d,e,f,g=[];if(!a._d){for(e=Q(a),a._w&&null==a._a[ub]&&null==a._a[tb]&&N(a),a._dayOfYear&&(f=b(a._a[sb],e[sb]),a._dayOfYear>x(f)&&(a._pf._overflowDayOfYear=!0),d=Z(f,0,a._dayOfYear),a._a[tb]=d.getUTCMonth(),a._a[ub]=d.getUTCDate()),c=0;3>c&&null==a._a[c];++c)a._a[c]=g[c]=e[c];for(;7>c;c++)a._a[c]=g[c]=null==a._a[c]?2===c?1:0:a._a[c];a._d=(a._useUTC?Z:Y).apply(null,g),null!=a._tzm&&a._d.setUTCMinutes(a._d.getUTCMinutes()+a._tzm)}}function P(a){var b;a._d||(b=s(a._i),a._a=[b.year,b.month,b.day,b.hour,b.minute,b.second,b.millisecond],O(a))}function Q(a){var b=new Date;return a._useUTC?[b.getUTCFullYear(),b.getUTCMonth(),b.getUTCDate()]:[b.getFullYear(),b.getMonth(),b.getDate()]}function R(a){if(a._f===mb.ISO_8601)return void V(a);a._a=[],a._pf.empty=!0;var b,c,d,e,f,g=F(a._l),h=""+a._i,i=h.length,j=0;for(d=J(a._f,g).match(Fb)||[],b=0;b0&&a._pf.unusedInput.push(f),h=h.slice(h.indexOf(c)+c.length),j+=c.length),hc[e]?(c?a._pf.empty=!1:a._pf.unusedTokens.push(e),M(e,c,a)):a._strict&&!c&&a._pf.unusedTokens.push(e);a._pf.charsLeftOver=i-j,h.length>0&&a._pf.unusedInput.push(h),a._isPm&&a._a[vb]<12&&(a._a[vb]+=12),a._isPm===!1&&12===a._a[vb]&&(a._a[vb]=0),O(a),z(a)}function S(a){return a.replace(/\\(\[)|\\(\])|\[([^\]\[]*)\]|\\(.)/g,function(a,b,c,d,e){return b||c||d||e})}function T(a){return a.replace(/[-\/\\^$*+?.()|[\]{}]/g,"\\$&")}function U(a){var b,d,e,f,g;if(0===a._f.length)return a._pf.invalidFormat=!0,void(a._d=new Date(0/0));for(f=0;fg)&&(e=g,d=b));j(a,d||b)}function V(a){var b,c,d=a._i,e=Xb.exec(d);if(e){for(a._pf.iso=!0,b=0,c=Zb.length;c>b;b++)if(Zb[b][1].exec(d)){a._f=Zb[b][0]+(e[6]||" ");break}for(b=0,c=$b.length;c>b;b++)if($b[b][1].exec(d)){a._f+=$b[b][0];break}d.match(Nb)&&(a._f+="Z"),R(a)}else a._isValid=!1}function W(a){V(a),a._isValid===!1&&(delete a._isValid,mb.createFromInputFallback(a))}function X(b){var c=b._i,d=Cb.exec(c);c===a?b._d=new Date:d?b._d=new Date(+d[1]):"string"==typeof c?W(b):o(c)?(b._a=c.slice(0),O(b)):p(c)?b._d=new Date(+c):"object"==typeof c?P(b):"number"==typeof c?b._d=new Date(c):mb.createFromInputFallback(b)}function Y(a,b,c,d,e,f,g){var h=new Date(a,b,c,d,e,f,g);return 1970>a&&h.setFullYear(a),h}function Z(a){var b=new Date(Date.UTC.apply(null,arguments));return 1970>a&&b.setUTCFullYear(a),b}function $(a,b){if("string"==typeof a)if(isNaN(a)){if(a=b.weekdaysParse(a),"number"!=typeof a)return null}else a=parseInt(a,10);return a}function _(a,b,c,d,e){return e.relativeTime(b||1,!!c,a,d)}function ab(a,b,c){var d=rb(Math.abs(a)/1e3),e=rb(d/60),f=rb(e/60),g=rb(f/24),h=rb(g/365),i=d0,i[4]=c,_.apply({},i)}function bb(a,b,c){var d,e=c-b,f=c-a.day();return f>e&&(f-=7),e-7>f&&(f+=7),d=mb(a).add("d",f),{week:Math.ceil(d.dayOfYear()/7),year:d.year()}}function cb(a,b,c,d,e){var f,g,h=Z(a,0,1).getUTCDay();return h=0===h?7:h,c=null!=c?c:e,f=e-h+(h>d?7:0)-(e>h?7:0),g=7*(b-1)+(c-e)+f+1,{year:g>0?a:a-1,dayOfYear:g>0?g:x(a-1)+g}}function db(b){var c=b._i,d=b._f;return null===c||d===a&&""===c?mb.invalid({nullInput:!0}):("string"==typeof c&&(b._i=c=F().preparse(c)),mb.isMoment(c)?(b=k(c),b._d=new Date(+c._d)):d?o(d)?U(b):R(b):X(b),new h(b))}function eb(a,b){var c,d;if(1===b.length&&o(b[0])&&(b=b[0]),!b.length)return mb();for(c=b[0],d=1;d=0?"+":"-";return b+m(Math.abs(a),6)},gg:function(){return m(this.weekYear()%100,2)},gggg:function(){return m(this.weekYear(),4)},ggggg:function(){return m(this.weekYear(),5)},GG:function(){return m(this.isoWeekYear()%100,2)},GGGG:function(){return m(this.isoWeekYear(),4)},GGGGG:function(){return m(this.isoWeekYear(),5)},e:function(){return this.weekday()},E:function(){return this.isoWeekday()},a:function(){return this.lang().meridiem(this.hours(),this.minutes(),!0)},A:function(){return this.lang().meridiem(this.hours(),this.minutes(),!1)},H:function(){return this.hours()},h:function(){return this.hours()%12||12},m:function(){return this.minutes()},s:function(){return this.seconds()},S:function(){return u(this.milliseconds()/100)},SS:function(){return m(u(this.milliseconds()/10),2)},SSS:function(){return m(this.milliseconds(),3)},SSSS:function(){return m(this.milliseconds(),3)},Z:function(){var a=-this.zone(),b="+";return 0>a&&(a=-a,b="-"),b+m(u(a/60),2)+":"+m(u(a)%60,2)},ZZ:function(){var a=-this.zone(),b="+";return 0>a&&(a=-a,b="-"),b+m(u(a/60),2)+m(u(a)%60,2)},z:function(){return this.zoneAbbr()},zz:function(){return this.zoneName()},X:function(){return this.unix()},Q:function(){return this.quarter()}},ic=["months","monthsShort","weekdays","weekdaysShort","weekdaysMin"];fc.length;)ob=fc.pop(),hc[ob+"o"]=f(hc[ob],ob);for(;gc.length;)ob=gc.pop(),hc[ob+ob]=e(hc[ob],2);for(hc.DDDD=e(hc.DDD,3),j(g.prototype,{set:function(a){var b,c;for(c in a)b=a[c],"function"==typeof b?this[c]=b:this["_"+c]=b},_months:"January_February_March_April_May_June_July_August_September_October_November_December".split("_"),months:function(a){return this._months[a.month()]},_monthsShort:"Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec".split("_"),monthsShort:function(a){return this._monthsShort[a.month()]},monthsParse:function(a){var b,c,d;for(this._monthsParse||(this._monthsParse=[]),b=0;12>b;b++)if(this._monthsParse[b]||(c=mb.utc([2e3,b]),d="^"+this.months(c,"")+"|^"+this.monthsShort(c,""),this._monthsParse[b]=new RegExp(d.replace(".",""),"i")),this._monthsParse[b].test(a))return b},_weekdays:"Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),weekdays:function(a){return this._weekdays[a.day()]},_weekdaysShort:"Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"),weekdaysShort:function(a){return this._weekdaysShort[a.day()]},_weekdaysMin:"Su_Mo_Tu_We_Th_Fr_Sa".split("_"),weekdaysMin:function(a){return this._weekdaysMin[a.day()]},weekdaysParse:function(a){var b,c,d;for(this._weekdaysParse||(this._weekdaysParse=[]),b=0;7>b;b++)if(this._weekdaysParse[b]||(c=mb([2e3,1]).day(b),d="^"+this.weekdays(c,"")+"|^"+this.weekdaysShort(c,"")+"|^"+this.weekdaysMin(c,""),this._weekdaysParse[b]=new RegExp(d.replace(".",""),"i")),this._weekdaysParse[b].test(a))return b},_longDateFormat:{LT:"h:mm A",L:"MM/DD/YYYY",LL:"MMMM D YYYY",LLL:"MMMM D YYYY LT",LLLL:"dddd, MMMM D YYYY LT"},longDateFormat:function(a){var b=this._longDateFormat[a];return!b&&this._longDateFormat[a.toUpperCase()]&&(b=this._longDateFormat[a.toUpperCase()].replace(/MMMM|MM|DD|dddd/g,function(a){return a.slice(1)}),this._longDateFormat[a]=b),b},isPM:function(a){return"p"===(a+"").toLowerCase().charAt(0)},_meridiemParse:/[ap]\.?m?\.?/i,meridiem:function(a,b,c){return a>11?c?"pm":"PM":c?"am":"AM"},_calendar:{sameDay:"[Today at] LT",nextDay:"[Tomorrow at] LT",nextWeek:"dddd [at] LT",lastDay:"[Yesterday at] LT",lastWeek:"[Last] dddd [at] LT",sameElse:"L"},calendar:function(a,b){var c=this._calendar[a];return"function"==typeof c?c.apply(b):c},_relativeTime:{future:"in %s",past:"%s ago",s:"a few seconds",m:"a minute",mm:"%d minutes",h:"an hour",hh:"%d hours",d:"a day",dd:"%d days",M:"a month",MM:"%d months",y:"a year",yy:"%d years"},relativeTime:function(a,b,c,d){var e=this._relativeTime[c];return"function"==typeof e?e(a,b,c,d):e.replace(/%d/i,a)},pastFuture:function(a,b){var c=this._relativeTime[a>0?"future":"past"];return"function"==typeof c?c(b):c.replace(/%s/i,b)},ordinal:function(a){return this._ordinal.replace("%d",a)},_ordinal:"%d",preparse:function(a){return a},postformat:function(a){return a},week:function(a){return bb(a,this._week.dow,this._week.doy).week},_week:{dow:0,doy:6},_invalidDate:"Invalid date",invalidDate:function(){return this._invalidDate}}),mb=function(b,d,e,f){var g;return"boolean"==typeof e&&(f=e,e=a),g={},g._isAMomentObject=!0,g._i=b,g._f=d,g._l=e,g._strict=f,g._isUTC=!1,g._pf=c(),db(g)},mb.suppressDeprecationWarnings=!1,mb.createFromInputFallback=d("moment construction falls back to js Date. This is discouraged and will be removed in upcoming major release. Please refer to https://github.com/moment/moment/issues/1407 for more info.",function(a){a._d=new Date(a._i)}),mb.min=function(){var a=[].slice.call(arguments,0);return eb("isBefore",a)},mb.max=function(){var a=[].slice.call(arguments,0);return eb("isAfter",a)},mb.utc=function(b,d,e,f){var g;return"boolean"==typeof e&&(f=e,e=a),g={},g._isAMomentObject=!0,g._useUTC=!0,g._isUTC=!0,g._l=e,g._i=b,g._f=d,g._strict=f,g._pf=c(),db(g).utc()},mb.unix=function(a){return mb(1e3*a)},mb.duration=function(a,b){var c,d,e,f=a,g=null;return mb.isDuration(a)?f={ms:a._milliseconds,d:a._days,M:a._months}:"number"==typeof a?(f={},b?f[b]=a:f.milliseconds=a):(g=Db.exec(a))?(c="-"===g[1]?-1:1,f={y:0,d:u(g[ub])*c,h:u(g[vb])*c,m:u(g[wb])*c,s:u(g[xb])*c,ms:u(g[yb])*c}):(g=Eb.exec(a))&&(c="-"===g[1]?-1:1,e=function(a){var b=a&&parseFloat(a.replace(",","."));return(isNaN(b)?0:b)*c},f={y:e(g[2]),M:e(g[3]),d:e(g[4]),h:e(g[5]),m:e(g[6]),s:e(g[7]),w:e(g[8])}),d=new i(f),mb.isDuration(a)&&a.hasOwnProperty("_lang")&&(d._lang=a._lang),d},mb.version=pb,mb.defaultFormat=Yb,mb.ISO_8601=function(){},mb.momentProperties=Ab,mb.updateOffset=function(){},mb.relativeTimeThreshold=function(b,c){return ec[b]===a?!1:(ec[b]=c,!0)},mb.lang=function(a,b){var c;return a?(b?D(B(a),b):null===b?(E(a),a="en"):zb[a]||F(a),c=mb.duration.fn._lang=mb.fn._lang=F(a),c._abbr):mb.fn._lang._abbr},mb.langData=function(a){return a&&a._lang&&a._lang._abbr&&(a=a._lang._abbr),F(a)},mb.isMoment=function(a){return a instanceof h||null!=a&&a.hasOwnProperty("_isAMomentObject")},mb.isDuration=function(a){return a instanceof i},ob=ic.length-1;ob>=0;--ob)t(ic[ob]);mb.normalizeUnits=function(a){return r(a)},mb.invalid=function(a){var b=mb.utc(0/0);return null!=a?j(b._pf,a):b._pf.userInvalidated=!0,b},mb.parseZone=function(){return mb.apply(null,arguments).parseZone()},mb.parseTwoDigitYear=function(a){return u(a)+(u(a)>68?1900:2e3)},j(mb.fn=h.prototype,{clone:function(){return mb(this)},valueOf:function(){return+this._d+6e4*(this._offset||0)},unix:function(){return Math.floor(+this/1e3)},toString:function(){return this.clone().lang("en").format("ddd MMM DD YYYY HH:mm:ss [GMT]ZZ")},toDate:function(){return this._offset?new Date(+this):this._d},toISOString:function(){var a=mb(this).utc();return 00:!1},parsingFlags:function(){return j({},this._pf)},invalidAt:function(){return this._pf.overflow},utc:function(){return this.zone(0)},local:function(){return this.zone(0),this._isUTC=!1,this},format:function(a){var b=I(this,a||mb.defaultFormat);return this.lang().postformat(b)},add:function(a,b){var c;return c="string"==typeof a&&"string"==typeof b?mb.duration(isNaN(+b)?+a:+b,isNaN(+b)?b:a):"string"==typeof a?mb.duration(+b,a):mb.duration(a,b),n(this,c,1),this},subtract:function(a,b){var c;return c="string"==typeof a&&"string"==typeof b?mb.duration(isNaN(+b)?+a:+b,isNaN(+b)?b:a):"string"==typeof a?mb.duration(+b,a):mb.duration(a,b),n(this,c,-1),this},diff:function(a,b,c){var d,e,f=C(a,this),g=6e4*(this.zone()-f.zone());return b=r(b),"year"===b||"month"===b?(d=432e5*(this.daysInMonth()+f.daysInMonth()),e=12*(this.year()-f.year())+(this.month()-f.month()),e+=(this-mb(this).startOf("month")-(f-mb(f).startOf("month")))/d,e-=6e4*(this.zone()-mb(this).startOf("month").zone()-(f.zone()-mb(f).startOf("month").zone()))/d,"year"===b&&(e/=12)):(d=this-f,e="second"===b?d/1e3:"minute"===b?d/6e4:"hour"===b?d/36e5:"day"===b?(d-g)/864e5:"week"===b?(d-g)/6048e5:d),c?e:l(e)},from:function(a,b){return mb.duration(this.diff(a)).lang(this.lang()._abbr).humanize(!b)},fromNow:function(a){return this.from(mb(),a)},calendar:function(a){var b=a||mb(),c=C(b,this).startOf("day"),d=this.diff(c,"days",!0),e=-6>d?"sameElse":-1>d?"lastWeek":0>d?"lastDay":1>d?"sameDay":2>d?"nextDay":7>d?"nextWeek":"sameElse";return this.format(this.lang().calendar(e,this))},isLeapYear:function(){return y(this.year())},isDST:function(){return this.zone()+mb(a).startOf(b)},isBefore:function(a,b){return b="undefined"!=typeof b?b:"millisecond",+this.clone().startOf(b)<+mb(a).startOf(b)},isSame:function(a,b){return b=b||"ms",+this.clone().startOf(b)===+C(a,this).startOf(b)},min:d("moment().min is deprecated, use moment.min instead. https://github.com/moment/moment/issues/1548",function(a){return a=mb.apply(null,arguments),this>a?this:a}),max:d("moment().max is deprecated, use moment.max instead. https://github.com/moment/moment/issues/1548",function(a){return a=mb.apply(null,arguments),a>this?this:a}),zone:function(a,b){var c=this._offset||0;return null==a?this._isUTC?c:this._d.getTimezoneOffset():("string"==typeof a&&(a=L(a)),Math.abs(a)<16&&(a=60*a),this._offset=a,this._isUTC=!0,c!==a&&(!b||this._changeInProgress?n(this,mb.duration(c-a,"m"),1,!1):this._changeInProgress||(this._changeInProgress=!0,mb.updateOffset(this,!0),this._changeInProgress=null)),this)},zoneAbbr:function(){return this._isUTC?"UTC":""},zoneName:function(){return this._isUTC?"Coordinated Universal Time":""},parseZone:function(){return this._tzm?this.zone(this._tzm):"string"==typeof this._i&&this.zone(this._i),this},hasAlignedHourOffset:function(a){return a=a?mb(a).zone():0,(this.zone()-a)%60===0},daysInMonth:function(){return v(this.year(),this.month())},dayOfYear:function(a){var b=rb((mb(this).startOf("day")-mb(this).startOf("year"))/864e5)+1;return null==a?b:this.add("d",a-b)},quarter:function(a){return null==a?Math.ceil((this.month()+1)/3):this.month(3*(a-1)+this.month()%3)},weekYear:function(a){var b=bb(this,this.lang()._week.dow,this.lang()._week.doy).year;return null==a?b:this.add("y",a-b)},isoWeekYear:function(a){var b=bb(this,1,4).year;return null==a?b:this.add("y",a-b)},week:function(a){var b=this.lang().week(this);return null==a?b:this.add("d",7*(a-b))},isoWeek:function(a){var b=bb(this,1,4).week;return null==a?b:this.add("d",7*(a-b))},weekday:function(a){var b=(this.day()+7-this.lang()._week.dow)%7;return null==a?b:this.add("d",a-b)},isoWeekday:function(a){return null==a?this.day()||7:this.day(this.day()%7?a:a-7)},isoWeeksInYear:function(){return w(this.year(),1,4)},weeksInYear:function(){var a=this._lang._week;return w(this.year(),a.dow,a.doy)},get:function(a){return a=r(a),this[a]()},set:function(a,b){return a=r(a),"function"==typeof this[a]&&this[a](b),this},lang:function(b){return b===a?this._lang:(this._lang=F(b),this)}}),mb.fn.millisecond=mb.fn.milliseconds=ib("Milliseconds",!1),mb.fn.second=mb.fn.seconds=ib("Seconds",!1),mb.fn.minute=mb.fn.minutes=ib("Minutes",!1),mb.fn.hour=mb.fn.hours=ib("Hours",!0),mb.fn.date=ib("Date",!0),mb.fn.dates=d("dates accessor is deprecated. Use date instead.",ib("Date",!0)),mb.fn.year=ib("FullYear",!0),mb.fn.years=d("years accessor is deprecated. Use year instead.",ib("FullYear",!0)),mb.fn.days=mb.fn.day,mb.fn.months=mb.fn.month,mb.fn.weeks=mb.fn.week,mb.fn.isoWeeks=mb.fn.isoWeek,mb.fn.quarters=mb.fn.quarter,mb.fn.toJSON=mb.fn.toISOString,j(mb.duration.fn=i.prototype,{_bubble:function(){var a,b,c,d,e=this._milliseconds,f=this._days,g=this._months,h=this._data;h.milliseconds=e%1e3,a=l(e/1e3),h.seconds=a%60,b=l(a/60),h.minutes=b%60,c=l(b/60),h.hours=c%24,f+=l(c/24),h.days=f%30,g+=l(f/30),h.months=g%12,d=l(g/12),h.years=d},weeks:function(){return l(this.days()/7)},valueOf:function(){return this._milliseconds+864e5*this._days+this._months%12*2592e6+31536e6*u(this._months/12)},humanize:function(a){var b=+this,c=ab(b,!a,this.lang());return a&&(c=this.lang().pastFuture(b,c)),this.lang().postformat(c)},add:function(a,b){var c=mb.duration(a,b);return this._milliseconds+=c._milliseconds,this._days+=c._days,this._months+=c._months,this._bubble(),this},subtract:function(a,b){var c=mb.duration(a,b);return this._milliseconds-=c._milliseconds,this._days-=c._days,this._months-=c._months,this._bubble(),this},get:function(a){return a=r(a),this[a.toLowerCase()+"s"]()},as:function(a){return a=r(a),this["as"+a.charAt(0).toUpperCase()+a.slice(1)+"s"]()},lang:mb.fn.lang,toIsoString:function(){var a=Math.abs(this.years()),b=Math.abs(this.months()),c=Math.abs(this.days()),d=Math.abs(this.hours()),e=Math.abs(this.minutes()),f=Math.abs(this.seconds()+this.milliseconds()/1e3);return this.asSeconds()?(this.asSeconds()<0?"-":"")+"P"+(a?a+"Y":"")+(b?b+"M":"")+(c?c+"D":"")+(d||e||f?"T":"")+(d?d+"H":"")+(e?e+"M":"")+(f?f+"S":""):"P0D"}});for(ob in ac)ac.hasOwnProperty(ob)&&(kb(ob,ac[ob]),jb(ob.toLowerCase()));kb("Weeks",6048e5),mb.duration.fn.asMonths=function(){return(+this-31536e6*this.years())/2592e6+12*this.years()},mb.lang("en",{ordinal:function(a){var b=a%10,c=1===u(a%100/10)?"th":1===b?"st":2===b?"nd":3===b?"rd":"th";return a+c}}),Bb?module.exports=mb:"function"==typeof define&&define.amd?(define("moment",function(a,b,c){return c.config&&c.config()&&c.config().noGlobal===!0&&(qb.moment=nb),mb}),lb(!0)):lb()}).call(this); \ No newline at end of file diff --git a/vendor/assets/javascripts/moment.min.js b/vendor/assets/javascripts/moment.min.js new file mode 100644 index 0000000000..3fe82adf13 --- /dev/null +++ b/vendor/assets/javascripts/moment.min.js @@ -0,0 +1,6 @@ +//! moment.js +//! version : 2.6.0 +//! authors : Tim Wood, Iskren Chernev, Moment.js contributors +//! license : MIT +//! momentjs.com +(function(a){function b(){return{empty:!1,unusedTokens:[],unusedInput:[],overflow:-2,charsLeftOver:0,nullInput:!1,invalidMonth:null,invalidFormat:!1,userInvalidated:!1,iso:!1}}function c(a,b){function c(){ib.suppressDeprecationWarnings===!1&&"undefined"!=typeof console&&console.warn&&console.warn("Deprecation warning: "+a)}var d=!0;return i(function(){return d&&(c(),d=!1),b.apply(this,arguments)},b)}function d(a,b){return function(c){return l(a.call(this,c),b)}}function e(a,b){return function(c){return this.lang().ordinal(a.call(this,c),b)}}function f(){}function g(a){y(a),i(this,a)}function h(a){var b=r(a),c=b.year||0,d=b.quarter||0,e=b.month||0,f=b.week||0,g=b.day||0,h=b.hour||0,i=b.minute||0,j=b.second||0,k=b.millisecond||0;this._milliseconds=+k+1e3*j+6e4*i+36e5*h,this._days=+g+7*f,this._months=+e+3*d+12*c,this._data={},this._bubble()}function i(a,b){for(var c in b)b.hasOwnProperty(c)&&(a[c]=b[c]);return b.hasOwnProperty("toString")&&(a.toString=b.toString),b.hasOwnProperty("valueOf")&&(a.valueOf=b.valueOf),a}function j(a){var b,c={};for(b in a)a.hasOwnProperty(b)&&wb.hasOwnProperty(b)&&(c[b]=a[b]);return c}function k(a){return 0>a?Math.ceil(a):Math.floor(a)}function l(a,b,c){for(var d=""+Math.abs(a),e=a>=0;d.lengthd;d++)(c&&a[d]!==b[d]||!c&&t(a[d])!==t(b[d]))&&g++;return g+f}function q(a){if(a){var b=a.toLowerCase().replace(/(.)s$/,"$1");a=Zb[a]||$b[b]||b}return a}function r(a){var b,c,d={};for(c in a)a.hasOwnProperty(c)&&(b=q(c),b&&(d[b]=a[c]));return d}function s(b){var c,d;if(0===b.indexOf("week"))c=7,d="day";else{if(0!==b.indexOf("month"))return;c=12,d="month"}ib[b]=function(e,f){var g,h,i=ib.fn._lang[b],j=[];if("number"==typeof e&&(f=e,e=a),h=function(a){var b=ib().utc().set(d,a);return i.call(ib.fn._lang,b,e||"")},null!=f)return h(f);for(g=0;c>g;g++)j.push(h(g));return j}}function t(a){var b=+a,c=0;return 0!==b&&isFinite(b)&&(c=b>=0?Math.floor(b):Math.ceil(b)),c}function u(a,b){return new Date(Date.UTC(a,b+1,0)).getUTCDate()}function v(a,b,c){return $(ib([a,11,31+b-c]),b,c).week}function w(a){return x(a)?366:365}function x(a){return a%4===0&&a%100!==0||a%400===0}function y(a){var b;a._a&&-2===a._pf.overflow&&(b=a._a[pb]<0||a._a[pb]>11?pb:a._a[qb]<1||a._a[qb]>u(a._a[ob],a._a[pb])?qb:a._a[rb]<0||a._a[rb]>23?rb:a._a[sb]<0||a._a[sb]>59?sb:a._a[tb]<0||a._a[tb]>59?tb:a._a[ub]<0||a._a[ub]>999?ub:-1,a._pf._overflowDayOfYear&&(ob>b||b>qb)&&(b=qb),a._pf.overflow=b)}function z(a){return null==a._isValid&&(a._isValid=!isNaN(a._d.getTime())&&a._pf.overflow<0&&!a._pf.empty&&!a._pf.invalidMonth&&!a._pf.nullInput&&!a._pf.invalidFormat&&!a._pf.userInvalidated,a._strict&&(a._isValid=a._isValid&&0===a._pf.charsLeftOver&&0===a._pf.unusedTokens.length)),a._isValid}function A(a){return a?a.toLowerCase().replace("_","-"):a}function B(a,b){return b._isUTC?ib(a).zone(b._offset||0):ib(a).local()}function C(a,b){return b.abbr=a,vb[a]||(vb[a]=new f),vb[a].set(b),vb[a]}function D(a){delete vb[a]}function E(a){var b,c,d,e,f=0,g=function(a){if(!vb[a]&&xb)try{require("./lang/"+a)}catch(b){}return vb[a]};if(!a)return ib.fn._lang;if(!n(a)){if(c=g(a))return c;a=[a]}for(;f0;){if(c=g(e.slice(0,b).join("-")))return c;if(d&&d.length>=b&&p(e,d,!0)>=b-1)break;b--}f++}return ib.fn._lang}function F(a){return a.match(/\[[\s\S]/)?a.replace(/^\[|\]$/g,""):a.replace(/\\/g,"")}function G(a){var b,c,d=a.match(Bb);for(b=0,c=d.length;c>b;b++)d[b]=cc[d[b]]?cc[d[b]]:F(d[b]);return function(e){var f="";for(b=0;c>b;b++)f+=d[b]instanceof Function?d[b].call(e,a):d[b];return f}}function H(a,b){return a.isValid()?(b=I(b,a.lang()),_b[b]||(_b[b]=G(b)),_b[b](a)):a.lang().invalidDate()}function I(a,b){function c(a){return b.longDateFormat(a)||a}var d=5;for(Cb.lastIndex=0;d>=0&&Cb.test(a);)a=a.replace(Cb,c),Cb.lastIndex=0,d-=1;return a}function J(a,b){var c,d=b._strict;switch(a){case"Q":return Nb;case"DDDD":return Pb;case"YYYY":case"GGGG":case"gggg":return d?Qb:Fb;case"Y":case"G":case"g":return Sb;case"YYYYYY":case"YYYYY":case"GGGGG":case"ggggg":return d?Rb:Gb;case"S":if(d)return Nb;case"SS":if(d)return Ob;case"SSS":if(d)return Pb;case"DDD":return Eb;case"MMM":case"MMMM":case"dd":case"ddd":case"dddd":return Ib;case"a":case"A":return E(b._l)._meridiemParse;case"X":return Lb;case"Z":case"ZZ":return Jb;case"T":return Kb;case"SSSS":return Hb;case"MM":case"DD":case"YY":case"GG":case"gg":case"HH":case"hh":case"mm":case"ss":case"ww":case"WW":return d?Ob:Db;case"M":case"D":case"d":case"H":case"h":case"m":case"s":case"w":case"W":case"e":case"E":return Db;case"Do":return Mb;default:return c=new RegExp(R(Q(a.replace("\\","")),"i"))}}function K(a){a=a||"";var b=a.match(Jb)||[],c=b[b.length-1]||[],d=(c+"").match(Xb)||["-",0,0],e=+(60*d[1])+t(d[2]);return"+"===d[0]?-e:e}function L(a,b,c){var d,e=c._a;switch(a){case"Q":null!=b&&(e[pb]=3*(t(b)-1));break;case"M":case"MM":null!=b&&(e[pb]=t(b)-1);break;case"MMM":case"MMMM":d=E(c._l).monthsParse(b),null!=d?e[pb]=d:c._pf.invalidMonth=b;break;case"D":case"DD":null!=b&&(e[qb]=t(b));break;case"Do":null!=b&&(e[qb]=t(parseInt(b,10)));break;case"DDD":case"DDDD":null!=b&&(c._dayOfYear=t(b));break;case"YY":e[ob]=ib.parseTwoDigitYear(b);break;case"YYYY":case"YYYYY":case"YYYYYY":e[ob]=t(b);break;case"a":case"A":c._isPm=E(c._l).isPM(b);break;case"H":case"HH":case"h":case"hh":e[rb]=t(b);break;case"m":case"mm":e[sb]=t(b);break;case"s":case"ss":e[tb]=t(b);break;case"S":case"SS":case"SSS":case"SSSS":e[ub]=t(1e3*("0."+b));break;case"X":c._d=new Date(1e3*parseFloat(b));break;case"Z":case"ZZ":c._useUTC=!0,c._tzm=K(b);break;case"w":case"ww":case"W":case"WW":case"d":case"dd":case"ddd":case"dddd":case"e":case"E":a=a.substr(0,1);case"gg":case"gggg":case"GG":case"GGGG":case"GGGGG":a=a.substr(0,2),b&&(c._w=c._w||{},c._w[a]=b)}}function M(a){var b,c,d,e,f,g,h,i,j,k,l=[];if(!a._d){for(d=O(a),a._w&&null==a._a[qb]&&null==a._a[pb]&&(f=function(b){var c=parseInt(b,10);return b?b.length<3?c>68?1900+c:2e3+c:c:null==a._a[ob]?ib().weekYear():a._a[ob]},g=a._w,null!=g.GG||null!=g.W||null!=g.E?h=_(f(g.GG),g.W||1,g.E,4,1):(i=E(a._l),j=null!=g.d?X(g.d,i):null!=g.e?parseInt(g.e,10)+i._week.dow:0,k=parseInt(g.w,10)||1,null!=g.d&&jw(e)&&(a._pf._overflowDayOfYear=!0),c=W(e,0,a._dayOfYear),a._a[pb]=c.getUTCMonth(),a._a[qb]=c.getUTCDate()),b=0;3>b&&null==a._a[b];++b)a._a[b]=l[b]=d[b];for(;7>b;b++)a._a[b]=l[b]=null==a._a[b]?2===b?1:0:a._a[b];l[rb]+=t((a._tzm||0)/60),l[sb]+=t((a._tzm||0)%60),a._d=(a._useUTC?W:V).apply(null,l)}}function N(a){var b;a._d||(b=r(a._i),a._a=[b.year,b.month,b.day,b.hour,b.minute,b.second,b.millisecond],M(a))}function O(a){var b=new Date;return a._useUTC?[b.getUTCFullYear(),b.getUTCMonth(),b.getUTCDate()]:[b.getFullYear(),b.getMonth(),b.getDate()]}function P(a){a._a=[],a._pf.empty=!0;var b,c,d,e,f,g=E(a._l),h=""+a._i,i=h.length,j=0;for(d=I(a._f,g).match(Bb)||[],b=0;b0&&a._pf.unusedInput.push(f),h=h.slice(h.indexOf(c)+c.length),j+=c.length),cc[e]?(c?a._pf.empty=!1:a._pf.unusedTokens.push(e),L(e,c,a)):a._strict&&!c&&a._pf.unusedTokens.push(e);a._pf.charsLeftOver=i-j,h.length>0&&a._pf.unusedInput.push(h),a._isPm&&a._a[rb]<12&&(a._a[rb]+=12),a._isPm===!1&&12===a._a[rb]&&(a._a[rb]=0),M(a),y(a)}function Q(a){return a.replace(/\\(\[)|\\(\])|\[([^\]\[]*)\]|\\(.)/g,function(a,b,c,d,e){return b||c||d||e})}function R(a){return a.replace(/[-\/\\^$*+?.()|[\]{}]/g,"\\$&")}function S(a){var c,d,e,f,g;if(0===a._f.length)return a._pf.invalidFormat=!0,void(a._d=new Date(0/0));for(f=0;fg)&&(e=g,d=c));i(a,d||c)}function T(a){var b,c,d=a._i,e=Tb.exec(d);if(e){for(a._pf.iso=!0,b=0,c=Vb.length;c>b;b++)if(Vb[b][1].exec(d)){a._f=Vb[b][0]+(e[6]||" ");break}for(b=0,c=Wb.length;c>b;b++)if(Wb[b][1].exec(d)){a._f+=Wb[b][0];break}d.match(Jb)&&(a._f+="Z"),P(a)}else ib.createFromInputFallback(a)}function U(b){var c=b._i,d=yb.exec(c);c===a?b._d=new Date:d?b._d=new Date(+d[1]):"string"==typeof c?T(b):n(c)?(b._a=c.slice(0),M(b)):o(c)?b._d=new Date(+c):"object"==typeof c?N(b):"number"==typeof c?b._d=new Date(c):ib.createFromInputFallback(b)}function V(a,b,c,d,e,f,g){var h=new Date(a,b,c,d,e,f,g);return 1970>a&&h.setFullYear(a),h}function W(a){var b=new Date(Date.UTC.apply(null,arguments));return 1970>a&&b.setUTCFullYear(a),b}function X(a,b){if("string"==typeof a)if(isNaN(a)){if(a=b.weekdaysParse(a),"number"!=typeof a)return null}else a=parseInt(a,10);return a}function Y(a,b,c,d,e){return e.relativeTime(b||1,!!c,a,d)}function Z(a,b,c){var d=nb(Math.abs(a)/1e3),e=nb(d/60),f=nb(e/60),g=nb(f/24),h=nb(g/365),i=45>d&&["s",d]||1===e&&["m"]||45>e&&["mm",e]||1===f&&["h"]||22>f&&["hh",f]||1===g&&["d"]||25>=g&&["dd",g]||45>=g&&["M"]||345>g&&["MM",nb(g/30)]||1===h&&["y"]||["yy",h];return i[2]=b,i[3]=a>0,i[4]=c,Y.apply({},i)}function $(a,b,c){var d,e=c-b,f=c-a.day();return f>e&&(f-=7),e-7>f&&(f+=7),d=ib(a).add("d",f),{week:Math.ceil(d.dayOfYear()/7),year:d.year()}}function _(a,b,c,d,e){var f,g,h=W(a,0,1).getUTCDay();return c=null!=c?c:e,f=e-h+(h>d?7:0)-(e>h?7:0),g=7*(b-1)+(c-e)+f+1,{year:g>0?a:a-1,dayOfYear:g>0?g:w(a-1)+g}}function ab(b){var c=b._i,d=b._f;return null===c||d===a&&""===c?ib.invalid({nullInput:!0}):("string"==typeof c&&(b._i=c=E().preparse(c)),ib.isMoment(c)?(b=j(c),b._d=new Date(+c._d)):d?n(d)?S(b):P(b):U(b),new g(b))}function bb(a,b){var c;return"string"==typeof b&&(b=a.lang().monthsParse(b),"number"!=typeof b)?a:(c=Math.min(a.date(),u(a.year(),b)),a._d["set"+(a._isUTC?"UTC":"")+"Month"](b,c),a)}function cb(a,b){return a._d["get"+(a._isUTC?"UTC":"")+b]()}function db(a,b,c){return"Month"===b?bb(a,c):a._d["set"+(a._isUTC?"UTC":"")+b](c)}function eb(a,b){return function(c){return null!=c?(db(this,a,c),ib.updateOffset(this,b),this):cb(this,a)}}function fb(a){ib.duration.fn[a]=function(){return this._data[a]}}function gb(a,b){ib.duration.fn["as"+a]=function(){return+this/b}}function hb(a){"undefined"==typeof ender&&(jb=mb.moment,mb.moment=a?c("Accessing Moment through the global scope is deprecated, and will be removed in an upcoming release.",ib):ib)}for(var ib,jb,kb,lb="2.6.0",mb="undefined"!=typeof global?global:this,nb=Math.round,ob=0,pb=1,qb=2,rb=3,sb=4,tb=5,ub=6,vb={},wb={_isAMomentObject:null,_i:null,_f:null,_l:null,_strict:null,_isUTC:null,_offset:null,_pf:null,_lang:null},xb="undefined"!=typeof module&&module.exports,yb=/^\/?Date\((\-?\d+)/i,zb=/(\-)?(?:(\d*)\.)?(\d+)\:(\d+)(?:\:(\d+)\.?(\d{3})?)?/,Ab=/^(-)?P(?:(?:([0-9,.]*)Y)?(?:([0-9,.]*)M)?(?:([0-9,.]*)D)?(?:T(?:([0-9,.]*)H)?(?:([0-9,.]*)M)?(?:([0-9,.]*)S)?)?|([0-9,.]*)W)$/,Bb=/(\[[^\[]*\])|(\\)?(Mo|MM?M?M?|Do|DDDo|DD?D?D?|ddd?d?|do?|w[o|w]?|W[o|W]?|Q|YYYYYY|YYYYY|YYYY|YY|gg(ggg?)?|GG(GGG?)?|e|E|a|A|hh?|HH?|mm?|ss?|S{1,4}|X|zz?|ZZ?|.)/g,Cb=/(\[[^\[]*\])|(\\)?(LT|LL?L?L?|l{1,4})/g,Db=/\d\d?/,Eb=/\d{1,3}/,Fb=/\d{1,4}/,Gb=/[+\-]?\d{1,6}/,Hb=/\d+/,Ib=/[0-9]*['a-z\u00A0-\u05FF\u0700-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+|[\u0600-\u06FF\/]+(\s*?[\u0600-\u06FF]+){1,2}/i,Jb=/Z|[\+\-]\d\d:?\d\d/gi,Kb=/T/i,Lb=/[\+\-]?\d+(\.\d{1,3})?/,Mb=/\d{1,2}/,Nb=/\d/,Ob=/\d\d/,Pb=/\d{3}/,Qb=/\d{4}/,Rb=/[+-]?\d{6}/,Sb=/[+-]?\d+/,Tb=/^\s*(?:[+-]\d{6}|\d{4})-(?:(\d\d-\d\d)|(W\d\d$)|(W\d\d-\d)|(\d\d\d))((T| )(\d\d(:\d\d(:\d\d(\.\d+)?)?)?)?([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?$/,Ub="YYYY-MM-DDTHH:mm:ssZ",Vb=[["YYYYYY-MM-DD",/[+-]\d{6}-\d{2}-\d{2}/],["YYYY-MM-DD",/\d{4}-\d{2}-\d{2}/],["GGGG-[W]WW-E",/\d{4}-W\d{2}-\d/],["GGGG-[W]WW",/\d{4}-W\d{2}/],["YYYY-DDD",/\d{4}-\d{3}/]],Wb=[["HH:mm:ss.SSSS",/(T| )\d\d:\d\d:\d\d\.\d+/],["HH:mm:ss",/(T| )\d\d:\d\d:\d\d/],["HH:mm",/(T| )\d\d:\d\d/],["HH",/(T| )\d\d/]],Xb=/([\+\-]|\d\d)/gi,Yb=("Date|Hours|Minutes|Seconds|Milliseconds".split("|"),{Milliseconds:1,Seconds:1e3,Minutes:6e4,Hours:36e5,Days:864e5,Months:2592e6,Years:31536e6}),Zb={ms:"millisecond",s:"second",m:"minute",h:"hour",d:"day",D:"date",w:"week",W:"isoWeek",M:"month",Q:"quarter",y:"year",DDD:"dayOfYear",e:"weekday",E:"isoWeekday",gg:"weekYear",GG:"isoWeekYear"},$b={dayofyear:"dayOfYear",isoweekday:"isoWeekday",isoweek:"isoWeek",weekyear:"weekYear",isoweekyear:"isoWeekYear"},_b={},ac="DDD w W M D d".split(" "),bc="M D H h m s w W".split(" "),cc={M:function(){return this.month()+1},MMM:function(a){return this.lang().monthsShort(this,a)},MMMM:function(a){return this.lang().months(this,a)},D:function(){return this.date()},DDD:function(){return this.dayOfYear()},d:function(){return this.day()},dd:function(a){return this.lang().weekdaysMin(this,a)},ddd:function(a){return this.lang().weekdaysShort(this,a)},dddd:function(a){return this.lang().weekdays(this,a)},w:function(){return this.week()},W:function(){return this.isoWeek()},YY:function(){return l(this.year()%100,2)},YYYY:function(){return l(this.year(),4)},YYYYY:function(){return l(this.year(),5)},YYYYYY:function(){var a=this.year(),b=a>=0?"+":"-";return b+l(Math.abs(a),6)},gg:function(){return l(this.weekYear()%100,2)},gggg:function(){return l(this.weekYear(),4)},ggggg:function(){return l(this.weekYear(),5)},GG:function(){return l(this.isoWeekYear()%100,2)},GGGG:function(){return l(this.isoWeekYear(),4)},GGGGG:function(){return l(this.isoWeekYear(),5)},e:function(){return this.weekday()},E:function(){return this.isoWeekday()},a:function(){return this.lang().meridiem(this.hours(),this.minutes(),!0)},A:function(){return this.lang().meridiem(this.hours(),this.minutes(),!1)},H:function(){return this.hours()},h:function(){return this.hours()%12||12},m:function(){return this.minutes()},s:function(){return this.seconds()},S:function(){return t(this.milliseconds()/100)},SS:function(){return l(t(this.milliseconds()/10),2)},SSS:function(){return l(this.milliseconds(),3)},SSSS:function(){return l(this.milliseconds(),3)},Z:function(){var a=-this.zone(),b="+";return 0>a&&(a=-a,b="-"),b+l(t(a/60),2)+":"+l(t(a)%60,2)},ZZ:function(){var a=-this.zone(),b="+";return 0>a&&(a=-a,b="-"),b+l(t(a/60),2)+l(t(a)%60,2)},z:function(){return this.zoneAbbr()},zz:function(){return this.zoneName()},X:function(){return this.unix()},Q:function(){return this.quarter()}},dc=["months","monthsShort","weekdays","weekdaysShort","weekdaysMin"];ac.length;)kb=ac.pop(),cc[kb+"o"]=e(cc[kb],kb);for(;bc.length;)kb=bc.pop(),cc[kb+kb]=d(cc[kb],2);for(cc.DDDD=d(cc.DDD,3),i(f.prototype,{set:function(a){var b,c;for(c in a)b=a[c],"function"==typeof b?this[c]=b:this["_"+c]=b},_months:"January_February_March_April_May_June_July_August_September_October_November_December".split("_"),months:function(a){return this._months[a.month()]},_monthsShort:"Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec".split("_"),monthsShort:function(a){return this._monthsShort[a.month()]},monthsParse:function(a){var b,c,d;for(this._monthsParse||(this._monthsParse=[]),b=0;12>b;b++)if(this._monthsParse[b]||(c=ib.utc([2e3,b]),d="^"+this.months(c,"")+"|^"+this.monthsShort(c,""),this._monthsParse[b]=new RegExp(d.replace(".",""),"i")),this._monthsParse[b].test(a))return b},_weekdays:"Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),weekdays:function(a){return this._weekdays[a.day()]},_weekdaysShort:"Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"),weekdaysShort:function(a){return this._weekdaysShort[a.day()]},_weekdaysMin:"Su_Mo_Tu_We_Th_Fr_Sa".split("_"),weekdaysMin:function(a){return this._weekdaysMin[a.day()]},weekdaysParse:function(a){var b,c,d;for(this._weekdaysParse||(this._weekdaysParse=[]),b=0;7>b;b++)if(this._weekdaysParse[b]||(c=ib([2e3,1]).day(b),d="^"+this.weekdays(c,"")+"|^"+this.weekdaysShort(c,"")+"|^"+this.weekdaysMin(c,""),this._weekdaysParse[b]=new RegExp(d.replace(".",""),"i")),this._weekdaysParse[b].test(a))return b},_longDateFormat:{LT:"h:mm A",L:"MM/DD/YYYY",LL:"MMMM D YYYY",LLL:"MMMM D YYYY LT",LLLL:"dddd, MMMM D YYYY LT"},longDateFormat:function(a){var b=this._longDateFormat[a];return!b&&this._longDateFormat[a.toUpperCase()]&&(b=this._longDateFormat[a.toUpperCase()].replace(/MMMM|MM|DD|dddd/g,function(a){return a.slice(1)}),this._longDateFormat[a]=b),b},isPM:function(a){return"p"===(a+"").toLowerCase().charAt(0)},_meridiemParse:/[ap]\.?m?\.?/i,meridiem:function(a,b,c){return a>11?c?"pm":"PM":c?"am":"AM"},_calendar:{sameDay:"[Today at] LT",nextDay:"[Tomorrow at] LT",nextWeek:"dddd [at] LT",lastDay:"[Yesterday at] LT",lastWeek:"[Last] dddd [at] LT",sameElse:"L"},calendar:function(a,b){var c=this._calendar[a];return"function"==typeof c?c.apply(b):c},_relativeTime:{future:"in %s",past:"%s ago",s:"a few seconds",m:"a minute",mm:"%d minutes",h:"an hour",hh:"%d hours",d:"a day",dd:"%d days",M:"a month",MM:"%d months",y:"a year",yy:"%d years"},relativeTime:function(a,b,c,d){var e=this._relativeTime[c];return"function"==typeof e?e(a,b,c,d):e.replace(/%d/i,a)},pastFuture:function(a,b){var c=this._relativeTime[a>0?"future":"past"];return"function"==typeof c?c(b):c.replace(/%s/i,b)},ordinal:function(a){return this._ordinal.replace("%d",a)},_ordinal:"%d",preparse:function(a){return a},postformat:function(a){return a},week:function(a){return $(a,this._week.dow,this._week.doy).week},_week:{dow:0,doy:6},_invalidDate:"Invalid date",invalidDate:function(){return this._invalidDate}}),ib=function(c,d,e,f){var g;return"boolean"==typeof e&&(f=e,e=a),g={},g._isAMomentObject=!0,g._i=c,g._f=d,g._l=e,g._strict=f,g._isUTC=!1,g._pf=b(),ab(g)},ib.suppressDeprecationWarnings=!1,ib.createFromInputFallback=c("moment construction falls back to js Date. This is discouraged and will be removed in upcoming major release. Please refer to https://github.com/moment/moment/issues/1407 for more info.",function(a){a._d=new Date(a._i)}),ib.utc=function(c,d,e,f){var g;return"boolean"==typeof e&&(f=e,e=a),g={},g._isAMomentObject=!0,g._useUTC=!0,g._isUTC=!0,g._l=e,g._i=c,g._f=d,g._strict=f,g._pf=b(),ab(g).utc()},ib.unix=function(a){return ib(1e3*a)},ib.duration=function(a,b){var c,d,e,f=a,g=null;return ib.isDuration(a)?f={ms:a._milliseconds,d:a._days,M:a._months}:"number"==typeof a?(f={},b?f[b]=a:f.milliseconds=a):(g=zb.exec(a))?(c="-"===g[1]?-1:1,f={y:0,d:t(g[qb])*c,h:t(g[rb])*c,m:t(g[sb])*c,s:t(g[tb])*c,ms:t(g[ub])*c}):(g=Ab.exec(a))&&(c="-"===g[1]?-1:1,e=function(a){var b=a&&parseFloat(a.replace(",","."));return(isNaN(b)?0:b)*c},f={y:e(g[2]),M:e(g[3]),d:e(g[4]),h:e(g[5]),m:e(g[6]),s:e(g[7]),w:e(g[8])}),d=new h(f),ib.isDuration(a)&&a.hasOwnProperty("_lang")&&(d._lang=a._lang),d},ib.version=lb,ib.defaultFormat=Ub,ib.momentProperties=wb,ib.updateOffset=function(){},ib.lang=function(a,b){var c;return a?(b?C(A(a),b):null===b?(D(a),a="en"):vb[a]||E(a),c=ib.duration.fn._lang=ib.fn._lang=E(a),c._abbr):ib.fn._lang._abbr},ib.langData=function(a){return a&&a._lang&&a._lang._abbr&&(a=a._lang._abbr),E(a)},ib.isMoment=function(a){return a instanceof g||null!=a&&a.hasOwnProperty("_isAMomentObject")},ib.isDuration=function(a){return a instanceof h},kb=dc.length-1;kb>=0;--kb)s(dc[kb]);ib.normalizeUnits=function(a){return q(a)},ib.invalid=function(a){var b=ib.utc(0/0);return null!=a?i(b._pf,a):b._pf.userInvalidated=!0,b},ib.parseZone=function(){return ib.apply(null,arguments).parseZone()},ib.parseTwoDigitYear=function(a){return t(a)+(t(a)>68?1900:2e3)},i(ib.fn=g.prototype,{clone:function(){return ib(this)},valueOf:function(){return+this._d+6e4*(this._offset||0)},unix:function(){return Math.floor(+this/1e3)},toString:function(){return this.clone().lang("en").format("ddd MMM DD YYYY HH:mm:ss [GMT]ZZ")},toDate:function(){return this._offset?new Date(+this):this._d},toISOString:function(){var a=ib(this).utc();return 00:!1},parsingFlags:function(){return i({},this._pf)},invalidAt:function(){return this._pf.overflow},utc:function(){return this.zone(0)},local:function(){return this.zone(0),this._isUTC=!1,this},format:function(a){var b=H(this,a||ib.defaultFormat);return this.lang().postformat(b)},add:function(a,b){var c;return c="string"==typeof a?ib.duration(+b,a):ib.duration(a,b),m(this,c,1),this},subtract:function(a,b){var c;return c="string"==typeof a?ib.duration(+b,a):ib.duration(a,b),m(this,c,-1),this},diff:function(a,b,c){var d,e,f=B(a,this),g=6e4*(this.zone()-f.zone());return b=q(b),"year"===b||"month"===b?(d=432e5*(this.daysInMonth()+f.daysInMonth()),e=12*(this.year()-f.year())+(this.month()-f.month()),e+=(this-ib(this).startOf("month")-(f-ib(f).startOf("month")))/d,e-=6e4*(this.zone()-ib(this).startOf("month").zone()-(f.zone()-ib(f).startOf("month").zone()))/d,"year"===b&&(e/=12)):(d=this-f,e="second"===b?d/1e3:"minute"===b?d/6e4:"hour"===b?d/36e5:"day"===b?(d-g)/864e5:"week"===b?(d-g)/6048e5:d),c?e:k(e)},from:function(a,b){return ib.duration(this.diff(a)).lang(this.lang()._abbr).humanize(!b)},fromNow:function(a){return this.from(ib(),a)},calendar:function(){var a=B(ib(),this).startOf("day"),b=this.diff(a,"days",!0),c=-6>b?"sameElse":-1>b?"lastWeek":0>b?"lastDay":1>b?"sameDay":2>b?"nextDay":7>b?"nextWeek":"sameElse";return this.format(this.lang().calendar(c,this))},isLeapYear:function(){return x(this.year())},isDST:function(){return this.zone()+ib(a).startOf(b)},isBefore:function(a,b){return b="undefined"!=typeof b?b:"millisecond",+this.clone().startOf(b)<+ib(a).startOf(b)},isSame:function(a,b){return b=b||"ms",+this.clone().startOf(b)===+B(a,this).startOf(b)},min:function(a){return a=ib.apply(null,arguments),this>a?this:a},max:function(a){return a=ib.apply(null,arguments),a>this?this:a},zone:function(a,b){var c=this._offset||0;return null==a?this._isUTC?c:this._d.getTimezoneOffset():("string"==typeof a&&(a=K(a)),Math.abs(a)<16&&(a=60*a),this._offset=a,this._isUTC=!0,c!==a&&(!b||this._changeInProgress?m(this,ib.duration(c-a,"m"),1,!1):this._changeInProgress||(this._changeInProgress=!0,ib.updateOffset(this,!0),this._changeInProgress=null)),this)},zoneAbbr:function(){return this._isUTC?"UTC":""},zoneName:function(){return this._isUTC?"Coordinated Universal Time":""},parseZone:function(){return this._tzm?this.zone(this._tzm):"string"==typeof this._i&&this.zone(this._i),this},hasAlignedHourOffset:function(a){return a=a?ib(a).zone():0,(this.zone()-a)%60===0},daysInMonth:function(){return u(this.year(),this.month())},dayOfYear:function(a){var b=nb((ib(this).startOf("day")-ib(this).startOf("year"))/864e5)+1;return null==a?b:this.add("d",a-b)},quarter:function(a){return null==a?Math.ceil((this.month()+1)/3):this.month(3*(a-1)+this.month()%3)},weekYear:function(a){var b=$(this,this.lang()._week.dow,this.lang()._week.doy).year;return null==a?b:this.add("y",a-b)},isoWeekYear:function(a){var b=$(this,1,4).year;return null==a?b:this.add("y",a-b)},week:function(a){var b=this.lang().week(this);return null==a?b:this.add("d",7*(a-b))},isoWeek:function(a){var b=$(this,1,4).week;return null==a?b:this.add("d",7*(a-b))},weekday:function(a){var b=(this.day()+7-this.lang()._week.dow)%7;return null==a?b:this.add("d",a-b)},isoWeekday:function(a){return null==a?this.day()||7:this.day(this.day()%7?a:a-7)},isoWeeksInYear:function(){return v(this.year(),1,4)},weeksInYear:function(){var a=this._lang._week;return v(this.year(),a.dow,a.doy)},get:function(a){return a=q(a),this[a]()},set:function(a,b){return a=q(a),"function"==typeof this[a]&&this[a](b),this},lang:function(b){return b===a?this._lang:(this._lang=E(b),this)}}),ib.fn.millisecond=ib.fn.milliseconds=eb("Milliseconds",!1),ib.fn.second=ib.fn.seconds=eb("Seconds",!1),ib.fn.minute=ib.fn.minutes=eb("Minutes",!1),ib.fn.hour=ib.fn.hours=eb("Hours",!0),ib.fn.date=eb("Date",!0),ib.fn.dates=c("dates accessor is deprecated. Use date instead.",eb("Date",!0)),ib.fn.year=eb("FullYear",!0),ib.fn.years=c("years accessor is deprecated. Use year instead.",eb("FullYear",!0)),ib.fn.days=ib.fn.day,ib.fn.months=ib.fn.month,ib.fn.weeks=ib.fn.week,ib.fn.isoWeeks=ib.fn.isoWeek,ib.fn.quarters=ib.fn.quarter,ib.fn.toJSON=ib.fn.toISOString,i(ib.duration.fn=h.prototype,{_bubble:function(){var a,b,c,d,e=this._milliseconds,f=this._days,g=this._months,h=this._data;h.milliseconds=e%1e3,a=k(e/1e3),h.seconds=a%60,b=k(a/60),h.minutes=b%60,c=k(b/60),h.hours=c%24,f+=k(c/24),h.days=f%30,g+=k(f/30),h.months=g%12,d=k(g/12),h.years=d},weeks:function(){return k(this.days()/7)},valueOf:function(){return this._milliseconds+864e5*this._days+this._months%12*2592e6+31536e6*t(this._months/12)},humanize:function(a){var b=+this,c=Z(b,!a,this.lang());return a&&(c=this.lang().pastFuture(b,c)),this.lang().postformat(c)},add:function(a,b){var c=ib.duration(a,b);return this._milliseconds+=c._milliseconds,this._days+=c._days,this._months+=c._months,this._bubble(),this},subtract:function(a,b){var c=ib.duration(a,b);return this._milliseconds-=c._milliseconds,this._days-=c._days,this._months-=c._months,this._bubble(),this},get:function(a){return a=q(a),this[a.toLowerCase()+"s"]()},as:function(a){return a=q(a),this["as"+a.charAt(0).toUpperCase()+a.slice(1)+"s"]()},lang:ib.fn.lang,toIsoString:function(){var a=Math.abs(this.years()),b=Math.abs(this.months()),c=Math.abs(this.days()),d=Math.abs(this.hours()),e=Math.abs(this.minutes()),f=Math.abs(this.seconds()+this.milliseconds()/1e3);return this.asSeconds()?(this.asSeconds()<0?"-":"")+"P"+(a?a+"Y":"")+(b?b+"M":"")+(c?c+"D":"")+(d||e||f?"T":"")+(d?d+"H":"")+(e?e+"M":"")+(f?f+"S":""):"P0D"}});for(kb in Yb)Yb.hasOwnProperty(kb)&&(gb(kb,Yb[kb]),fb(kb.toLowerCase()));gb("Weeks",6048e5),ib.duration.fn.asMonths=function(){return(+this-31536e6*this.years())/2592e6+12*this.years()},ib.lang("en",{ordinal:function(a){var b=a%10,c=1===t(a%100/10)?"th":1===b?"st":2===b?"nd":3===b?"rd":"th";return a+c}}),xb?module.exports=ib:"function"==typeof define&&define.amd?(define("moment",function(a,b,c){return c.config&&c.config()&&c.config().noGlobal===!0&&(mb.moment=jb),ib}),hb(!0)):hb()}).call(this); \ No newline at end of file diff --git a/vendor/assets/javascripts/propertyParser.js b/vendor/assets/javascripts/propertyParser.js new file mode 100644 index 0000000000..7cf589a616 --- /dev/null +++ b/vendor/assets/javascripts/propertyParser.js @@ -0,0 +1,43 @@ +/** + * Basic parser for URL properties + * @author Miller Medeiros + * @version 0.1.0 (2011/12/06) + * MIT license + */ +define(function(){ + + var rProps = /([\w-]+)\s*:\s*(?:(\[[^\]]+\])|([^,]+)),?/g, //match "foo:bar" and "lorem:[ipsum,dolor]" capturing name as $1 and val as $2 or $3 + rArr = /^\[([^\]]+)\]$/; //match "[foo,bar]" capturing "foo,bar" + + function parseProperties(str){ + var match, obj = {}; + while (match = rProps.exec(str)) { + obj[ match[1] ] = typecastVal(match[2] || match[3]); + } + return obj; + } + + function typecastVal(val){ + if (rArr.test(val)){ + val = val.replace(rArr, '$1').split(','); + } else if (val === 'null'){ + val = null; + } else if (val === 'false'){ + val = false; + } else if (val === 'true'){ + val = true; + } else if (val === '' || val === "''" || val === '""'){ + val = ''; + } else if (! isNaN(val)) { + //isNaN('') == false + val = +val; + } + return val; + } + + //API + return { + parseProperties : parseProperties, + typecastVal : typecastVal + }; +}); diff --git a/vendor/assets/javascripts/require.js b/vendor/assets/javascripts/require.js new file mode 100644 index 0000000000..0c6f152808 --- /dev/null +++ b/vendor/assets/javascripts/require.js @@ -0,0 +1,2037 @@ +/** vim: et:ts=4:sw=4:sts=4 + * @license RequireJS 2.0.2 Copyright (c) 2010-2012, The Dojo Foundation All Rights Reserved. + * Available via the MIT or new BSD license. + * see: http://github.com/jrburke/requirejs for details + */ +/*jslint regexp: true, nomen: true */ +/*global window, navigator, document, importScripts, jQuery, setTimeout, opera */ + +var requirejs, require, define; +(function (global) { + 'use strict'; + + var version = '2.0.2', + commentRegExp = /(\/\*([\s\S]*?)\*\/|([^:]|^)\/\/(.*)$)/mg, + cjsRequireRegExp = /require\s*\(\s*["']([^'"\s]+)["']\s*\)/g, + jsSuffixRegExp = /\.js$/, + currDirRegExp = /^\.\//, + ostring = Object.prototype.toString, + ap = Array.prototype, + aps = ap.slice, + apsp = ap.splice, + isBrowser = !!(typeof window !== 'undefined' && navigator && document), + isWebWorker = !isBrowser && typeof importScripts !== 'undefined', + //PS3 indicates loaded and complete, but need to wait for complete + //specifically. Sequence is 'loading', 'loaded', execution, + // then 'complete'. The UA check is unfortunate, but not sure how + //to feature test w/o causing perf issues. + readyRegExp = isBrowser && navigator.platform === 'PLAYSTATION 3' ? + /^complete$/ : /^(complete|loaded)$/, + defContextName = '_', + //Oh the tragedy, detecting opera. See the usage of isOpera for reason. + isOpera = typeof opera !== 'undefined' && opera.toString() === '[object Opera]', + contexts = {}, + cfg = {}, + globalDefQueue = [], + useInteractive = false, + req, s, head, baseElement, dataMain, src, + interactiveScript, currentlyAddingScript, mainScript, subPath; + + function isFunction(it) { + return ostring.call(it) === '[object Function]'; + } + + function isArray(it) { + return ostring.call(it) === '[object Array]'; + } + + /** + * Helper function for iterating over an array. If the func returns + * a true value, it will break out of the loop. + */ + function each(ary, func) { + if (ary) { + var i; + for (i = 0; i < ary.length; i += 1) { + if (ary[i] && func(ary[i], i, ary)) { + break; + } + } + } + } + + /** + * Helper function for iterating over an array backwards. If the func + * returns a true value, it will break out of the loop. + */ + function eachReverse(ary, func) { + if (ary) { + var i; + for (i = ary.length - 1; i > -1; i -= 1) { + if (ary[i] && func(ary[i], i, ary)) { + break; + } + } + } + } + + function hasProp(obj, prop) { + return obj.hasOwnProperty(prop); + } + + /** + * Cycles over properties in an object and calls a function for each + * property value. If the function returns a truthy value, then the + * iteration is stopped. + */ + function eachProp(obj, func) { + var prop; + for (prop in obj) { + if (obj.hasOwnProperty(prop)) { + if (func(obj[prop], prop)) { + break; + } + } + } + } + + /** + * Simple function to mix in properties from source into target, + * but only if target does not already have a property of the same name. + * This is not robust in IE for transferring methods that match + * Object.prototype names, but the uses of mixin here seem unlikely to + * trigger a problem related to that. + */ + function mixin(target, source, force, deepStringMixin) { + if (source) { + eachProp(source, function (value, prop) { + if (force || !hasProp(target, prop)) { + if (deepStringMixin && typeof value !== 'string') { + if (!target[prop]) { + target[prop] = {}; + } + mixin(target[prop], value, force, deepStringMixin); + } else { + target[prop] = value; + } + } + }); + } + return target; + } + + //Similar to Function.prototype.bind, but the 'this' object is specified + //first, since it is easier to read/figure out what 'this' will be. + function bind(obj, fn) { + return function () { + return fn.apply(obj, arguments); + }; + } + + function scripts() { + return document.getElementsByTagName('script'); + } + + //Allow getting a global that expressed in + //dot notation, like 'a.b.c'. + function getGlobal(value) { + if (!value) { + return value; + } + var g = global; + each(value.split('.'), function (part) { + g = g[part]; + }); + return g; + } + + function makeContextModuleFunc(func, relMap, enableBuildCallback) { + return function () { + //A version of a require function that passes a moduleName + //value for items that may need to + //look up paths relative to the moduleName + var args = aps.call(arguments, 0), lastArg; + if (enableBuildCallback && + isFunction((lastArg = args[args.length - 1]))) { + lastArg.__requireJsBuild = true; + } + args.push(relMap); + return func.apply(null, args); + }; + } + + function addRequireMethods(req, context, relMap) { + each([ + ['toUrl'], + ['undef'], + ['defined', 'requireDefined'], + ['specified', 'requireSpecified'] + ], function (item) { + var prop = item[1] || item[0]; + req[item[0]] = context ? makeContextModuleFunc(context[prop], relMap) : + //If no context, then use default context. Reference from + //contexts instead of early binding to default context, so + //that during builds, the latest instance of the default + //context with its config gets used. + function () { + var ctx = contexts[defContextName]; + return ctx[prop].apply(ctx, arguments); + }; + }); + } + + /** + * Constructs an error with a pointer to an URL with more information. + * @param {String} id the error ID that maps to an ID on a web page. + * @param {String} message human readable error. + * @param {Error} [err] the original error, if there is one. + * + * @returns {Error} + */ + function makeError(id, msg, err, requireModules) { + var e = new Error(msg + '\nhttp://requirejs.org/docs/errors.html#' + id); + e.requireType = id; + e.requireModules = requireModules; + if (err) { + e.originalError = err; + } + return e; + } + + if (typeof define !== 'undefined') { + //If a define is already in play via another AMD loader, + //do not overwrite. + return; + } + + if (typeof requirejs !== 'undefined') { + if (isFunction(requirejs)) { + //Do not overwrite and existing requirejs instance. + return; + } + cfg = requirejs; + requirejs = undefined; + } + + //Allow for a require config object + if (typeof require !== 'undefined' && !isFunction(require)) { + //assume it is a config object. + cfg = require; + require = undefined; + } + + function newContext(contextName) { + var config = { + waitSeconds: 7, + baseUrl: './', + paths: {}, + pkgs: {}, + shim: {} + }, + registry = {}, + undefEvents = {}, + defQueue = [], + defined = {}, + urlFetched = {}, + requireCounter = 1, + unnormalizedCounter = 1, + //Used to track the order in which modules + //should be executed, by the order they + //load. Important for consistent cycle resolution + //behavior. + waitAry = [], + inCheckLoaded, Module, context, handlers, + checkLoadedTimeoutId; + + /** + * Trims the . and .. from an array of path segments. + * It will keep a leading path segment if a .. will become + * the first path segment, to help with module name lookups, + * which act like paths, but can be remapped. But the end result, + * all paths that use this function should look normalized. + * NOTE: this method MODIFIES the input array. + * @param {Array} ary the array of path segments. + */ + function trimDots(ary) { + var i, part; + for (i = 0; ary[i]; i+= 1) { + part = ary[i]; + if (part === '.') { + ary.splice(i, 1); + i -= 1; + } else if (part === '..') { + if (i === 1 && (ary[2] === '..' || ary[0] === '..')) { + //End of the line. Keep at least one non-dot + //path segment at the front so it can be mapped + //correctly to disk. Otherwise, there is likely + //no path mapping for a path starting with '..'. + //This can still fail, but catches the most reasonable + //uses of .. + break; + } else if (i > 0) { + ary.splice(i - 1, 2); + i -= 2; + } + } + } + } + + /** + * Given a relative module name, like ./something, normalize it to + * a real name that can be mapped to a path. + * @param {String} name the relative name + * @param {String} baseName a real name that the name arg is relative + * to. + * @param {Boolean} applyMap apply the map config to the value. Should + * only be done if this normalization is for a dependency ID. + * @returns {String} normalized name + */ + function normalize(name, baseName, applyMap) { + var baseParts = baseName && baseName.split('/'), + map = config.map, + starMap = map && map['*'], + pkgName, pkgConfig, mapValue, nameParts, i, j, nameSegment, + foundMap; + + //Adjust any relative paths. + if (name && name.charAt(0) === '.') { + //If have a base name, try to normalize against it, + //otherwise, assume it is a top-level require that will + //be relative to baseUrl in the end. + if (baseName) { + if (config.pkgs[baseName]) { + //If the baseName is a package name, then just treat it as one + //name to concat the name with. + baseParts = [baseName]; + } else { + //Convert baseName to array, and lop off the last part, + //so that . matches that 'directory' and not name of the baseName's + //module. For instance, baseName of 'one/two/three', maps to + //'one/two/three.js', but we want the directory, 'one/two' for + //this normalization. + baseParts = baseParts.slice(0, baseParts.length - 1); + } + + name = baseParts.concat(name.split('/')); + trimDots(name); + + //Some use of packages may use a . path to reference the + //'main' module name, so normalize for that. + pkgConfig = config.pkgs[(pkgName = name[0])]; + name = name.join('/'); + if (pkgConfig && name === pkgName + '/' + pkgConfig.main) { + name = pkgName; + } + } else if (name.indexOf('./') === 0) { + // No baseName, so this is ID is resolved relative + // to baseUrl, pull off the leading dot. + name = name.substring(2); + } + } + + //Apply map config if available. + if (applyMap && (baseParts || starMap) && map) { + nameParts = name.split('/'); + + for (i = nameParts.length; i > 0; i -= 1) { + nameSegment = nameParts.slice(0, i).join('/'); + + if (baseParts) { + //Find the longest baseName segment match in the config. + //So, do joins on the biggest to smallest lengths of baseParts. + for (j = baseParts.length; j > 0; j -= 1) { + mapValue = map[baseParts.slice(0, j).join('/')]; + + //baseName segment has config, find if it has one for + //this name. + if (mapValue) { + mapValue = mapValue[nameSegment]; + if (mapValue) { + //Match, update name to the new value. + foundMap = mapValue; + break; + } + } + } + } + + if (!foundMap && starMap && starMap[nameSegment]) { + foundMap = starMap[nameSegment]; + } + + if (foundMap) { + nameParts.splice(0, i, foundMap); + name = nameParts.join('/'); + break; + } + } + } + + return name; + } + + function removeScript(name) { + if (isBrowser) { + each(scripts(), function (scriptNode) { + if (scriptNode.getAttribute('data-requiremodule') === name && + scriptNode.getAttribute('data-requirecontext') === context.contextName) { + scriptNode.parentNode.removeChild(scriptNode); + return true; + } + }); + } + } + + function hasPathFallback(id) { + var pathConfig = config.paths[id]; + if (pathConfig && isArray(pathConfig) && pathConfig.length > 1) { + removeScript(id); + //Pop off the first array value, since it failed, and + //retry + pathConfig.shift(); + context.undef(id); + context.require([id]); + return true; + } + } + + /** + * Creates a module mapping that includes plugin prefix, module + * name, and path. If parentModuleMap is provided it will + * also normalize the name via require.normalize() + * + * @param {String} name the module name + * @param {String} [parentModuleMap] parent module map + * for the module name, used to resolve relative names. + * @param {Boolean} isNormalized: is the ID already normalized. + * This is true if this call is done for a define() module ID. + * @param {Boolean} applyMap: apply the map config to the ID. + * Should only be true if this map is for a dependency. + * + * @returns {Object} + */ + function makeModuleMap(name, parentModuleMap, isNormalized, applyMap) { + var index = name ? name.indexOf('!') : -1, + prefix = null, + parentName = parentModuleMap ? parentModuleMap.name : null, + originalName = name, + isDefine = true, + normalizedName = '', + url, pluginModule, suffix; + + //If no name, then it means it is a require call, generate an + //internal name. + if (!name) { + isDefine = false; + name = '_@r' + (requireCounter += 1); + } + + if (index !== -1) { + prefix = name.substring(0, index); + name = name.substring(index + 1, name.length); + } + + if (prefix) { + prefix = normalize(prefix, parentName, applyMap); + pluginModule = defined[prefix]; + } + + //Account for relative paths if there is a base name. + if (name) { + if (prefix) { + if (pluginModule && pluginModule.normalize) { + //Plugin is loaded, use its normalize method. + normalizedName = pluginModule.normalize(name, function (name) { + return normalize(name, parentName, applyMap); + }); + } else { + normalizedName = normalize(name, parentName, applyMap); + } + } else { + //A regular module. + normalizedName = normalize(name, parentName, applyMap); + + //Calculate url for the module, if it has a name. + //Use name here since nameToUrl also calls normalize, + //and for relative names that are outside the baseUrl + //this causes havoc. Was thinking of just removing + //parentModuleMap to avoid extra normalization, but + //normalize() still does a dot removal because of + //issue #142, so just pass in name here and redo + //the normalization. Paths outside baseUrl are just + //messy to support. + url = context.nameToUrl(name, null, parentModuleMap); + } + } + + //If the id is a plugin id that cannot be determined if it needs + //normalization, stamp it with a unique ID so two matching relative + //ids that may conflict can be separate. + suffix = prefix && !pluginModule && !isNormalized ? + '_unnormalized' + (unnormalizedCounter += 1) : + ''; + + return { + prefix: prefix, + name: normalizedName, + parentMap: parentModuleMap, + unnormalized: !!suffix, + url: url, + originalName: originalName, + isDefine: isDefine, + id: (prefix ? + prefix + '!' + normalizedName : + normalizedName) + suffix + }; + } + + function getModule(depMap) { + var id = depMap.id, + mod = registry[id]; + + if (!mod) { + mod = registry[id] = new context.Module(depMap); + } + + return mod; + } + + function on(depMap, name, fn) { + var id = depMap.id, + mod = registry[id]; + + if (hasProp(defined, id) && + (!mod || mod.defineEmitComplete)) { + if (name === 'defined') { + fn(defined[id]); + } + } else { + getModule(depMap).on(name, fn); + } + } + + function onError(err, errback) { + var ids = err.requireModules, + notified = false; + + if (errback) { + errback(err); + } else { + each(ids, function (id) { + var mod = registry[id]; + if (mod) { + //Set error on module, so it skips timeout checks. + mod.error = err; + if (mod.events.error) { + notified = true; + mod.emit('error', err); + } + } + }); + + if (!notified) { + req.onError(err); + } + } + } + + /** + * Internal method to transfer globalQueue items to this context's + * defQueue. + */ + function takeGlobalQueue() { + //Push all the globalDefQueue items into the context's defQueue + if (globalDefQueue.length) { + //Array splice in the values since the context code has a + //local var ref to defQueue, so cannot just reassign the one + //on context. + apsp.apply(defQueue, + [defQueue.length - 1, 0].concat(globalDefQueue)); + globalDefQueue = []; + } + } + + /** + * Helper function that creates a require function object to give to + * modules that ask for it as a dependency. It needs to be specific + * per module because of the implication of path mappings that may + * need to be relative to the module name. + */ + function makeRequire(mod, enableBuildCallback, altRequire) { + var relMap = mod && mod.map, + modRequire = makeContextModuleFunc(altRequire || context.require, + relMap, + enableBuildCallback); + + addRequireMethods(modRequire, context, relMap); + modRequire.isBrowser = isBrowser; + + return modRequire; + } + + handlers = { + 'require': function (mod) { + return makeRequire(mod); + }, + 'exports': function (mod) { + mod.usingExports = true; + if (mod.map.isDefine) { + return (mod.exports = defined[mod.map.id] = {}); + } + }, + 'module': function (mod) { + return (mod.module = { + id: mod.map.id, + uri: mod.map.url, + config: function () { + return (config.config && config.config[mod.map.id]) || {}; + }, + exports: defined[mod.map.id] + }); + } + }; + + function removeWaiting(id) { + //Clean up machinery used for waiting modules. + delete registry[id]; + + each(waitAry, function (mod, i) { + if (mod.map.id === id) { + waitAry.splice(i, 1); + if (!mod.defined) { + context.waitCount -= 1; + } + return true; + } + }); + } + + function findCycle(mod, traced) { + var id = mod.map.id, + depArray = mod.depMaps, + foundModule; + + //Do not bother with unitialized modules or not yet enabled + //modules. + if (!mod.inited) { + return; + } + + //Found the cycle. + if (traced[id]) { + return mod; + } + + traced[id] = true; + + //Trace through the dependencies. + each(depArray, function (depMap) { + var depId = depMap.id, + depMod = registry[depId]; + + if (!depMod) { + return; + } + + if (!depMod.inited || !depMod.enabled) { + //Dependency is not inited, so this cannot + //be used to determine a cycle. + foundModule = null; + delete traced[id]; + return true; + } + + //mixin traced to a new object for each dependency, so that + //sibling dependencies in this object to not generate a + //false positive match on a cycle. Ideally an Object.create + //type of prototype delegation would be used here, but + //optimizing for file size vs. execution speed since hopefully + //the trees are small for circular dependency scans relative + //to the full app perf. + return (foundModule = findCycle(depMod, mixin({}, traced))); + }); + + return foundModule; + } + + function forceExec(mod, traced, uninited) { + var id = mod.map.id, + depArray = mod.depMaps; + + if (!mod.inited || !mod.map.isDefine) { + return; + } + + if (traced[id]) { + return defined[id]; + } + + traced[id] = mod; + + each(depArray, function(depMap) { + var depId = depMap.id, + depMod = registry[depId], + value; + + if (handlers[depId]) { + return; + } + + if (depMod) { + if (!depMod.inited || !depMod.enabled) { + //Dependency is not inited, + //so this module cannot be + //given a forced value yet. + uninited[id] = true; + return; + } + + //Get the value for the current dependency + value = forceExec(depMod, traced, uninited); + + //Even with forcing it may not be done, + //in particular if the module is waiting + //on a plugin resource. + if (!uninited[depId]) { + mod.defineDepById(depId, value); + } + } + }); + + mod.check(true); + + return defined[id]; + } + + function modCheck(mod) { + mod.check(); + } + + function checkLoaded() { + var waitInterval = config.waitSeconds * 1000, + //It is possible to disable the wait interval by using waitSeconds of 0. + expired = waitInterval && (context.startTime + waitInterval) < new Date().getTime(), + noLoads = [], + stillLoading = false, + needCycleCheck = true, + map, modId, err, usingPathFallback; + + //Do not bother if this call was a result of a cycle break. + if (inCheckLoaded) { + return; + } + + inCheckLoaded = true; + + //Figure out the state of all the modules. + eachProp(registry, function (mod) { + map = mod.map; + modId = map.id; + + //Skip things that are not enabled or in error state. + if (!mod.enabled) { + return; + } + + if (!mod.error) { + //If the module should be executed, and it has not + //been inited and time is up, remember it. + if (!mod.inited && expired) { + if (hasPathFallback(modId)) { + usingPathFallback = true; + stillLoading = true; + } else { + noLoads.push(modId); + removeScript(modId); + } + } else if (!mod.inited && mod.fetched && map.isDefine) { + stillLoading = true; + if (!map.prefix) { + //No reason to keep looking for unfinished + //loading. If the only stillLoading is a + //plugin resource though, keep going, + //because it may be that a plugin resource + //is waiting on a non-plugin cycle. + return (needCycleCheck = false); + } + } + } + }); + + if (expired && noLoads.length) { + //If wait time expired, throw error of unloaded modules. + err = makeError('timeout', 'Load timeout for modules: ' + noLoads, null, noLoads); + err.contextName = context.contextName; + return onError(err); + } + + //Not expired, check for a cycle. + if (needCycleCheck) { + + each(waitAry, function (mod) { + if (mod.defined) { + return; + } + + var cycleMod = findCycle(mod, {}), + traced = {}; + + if (cycleMod) { + forceExec(cycleMod, traced, {}); + + //traced modules may have been + //removed from the registry, but + //their listeners still need to + //be called. + eachProp(traced, modCheck); + } + }); + + //Now that dependencies have + //been satisfied, trigger the + //completion check that then + //notifies listeners. + eachProp(registry, modCheck); + } + + //If still waiting on loads, and the waiting load is something + //other than a plugin resource, or there are still outstanding + //scripts, then just try back later. + if ((!expired || usingPathFallback) && stillLoading) { + //Something is still waiting to load. Wait for it, but only + //if a timeout is not already in effect. + if ((isBrowser || isWebWorker) && !checkLoadedTimeoutId) { + checkLoadedTimeoutId = setTimeout(function () { + checkLoadedTimeoutId = 0; + checkLoaded(); + }, 50); + } + } + + inCheckLoaded = false; + } + + Module = function (map) { + this.events = undefEvents[map.id] || {}; + this.map = map; + this.shim = config.shim[map.id]; + this.depExports = []; + this.depMaps = []; + this.depMatched = []; + this.pluginMaps = {}; + this.depCount = 0; + + /* this.exports this.factory + this.depMaps = [], + this.enabled, this.fetched + */ + }; + + Module.prototype = { + init: function(depMaps, factory, errback, options) { + options = options || {}; + + //Do not do more inits if already done. Can happen if there + //are multiple define calls for the same module. That is not + //a normal, common case, but it is also not unexpected. + if (this.inited) { + return; + } + + this.factory = factory; + + if (errback) { + //Register for errors on this module. + this.on('error', errback); + } else if (this.events.error) { + //If no errback already, but there are error listeners + //on this module, set up an errback to pass to the deps. + errback = bind(this, function (err) { + this.emit('error', err); + }); + } + + //Do a copy of the dependency array, so that + //source inputs are not modified. For example + //"shim" deps are passed in here directly, and + //doing a direct modification of the depMaps array + //would affect that config. + this.depMaps = depMaps && depMaps.slice(0); + this.depMaps.rjsSkipMap = depMaps.rjsSkipMap; + + this.errback = errback; + + //Indicate this module has be initialized + this.inited = true; + + this.ignore = options.ignore; + + //Could have option to init this module in enabled mode, + //or could have been previously marked as enabled. However, + //the dependencies are not known until init is called. So + //if enabled previously, now trigger dependencies as enabled. + if (options.enabled || this.enabled) { + //Enable this module and dependencies. + //Will call this.check() + this.enable(); + } else { + this.check(); + } + }, + + defineDepById: function (id, depExports) { + var i; + + //Find the index for this dependency. + each(this.depMaps, function (map, index) { + if (map.id === id) { + i = index; + return true; + } + }); + + return this.defineDep(i, depExports); + }, + + defineDep: function (i, depExports) { + //Because of cycles, defined callback for a given + //export can be called more than once. + if (!this.depMatched[i]) { + this.depMatched[i] = true; + this.depCount -= 1; + this.depExports[i] = depExports; + } + }, + + fetch: function () { + if (this.fetched) { + return; + } + this.fetched = true; + + context.startTime = (new Date()).getTime(); + + var map = this.map; + + //If the manager is for a plugin managed resource, + //ask the plugin to load it now. + if (this.shim) { + makeRequire(this, true)(this.shim.deps || [], bind(this, function () { + return map.prefix ? this.callPlugin() : this.load(); + })); + } else { + //Regular dependency. + return map.prefix ? this.callPlugin() : this.load(); + } + }, + + load: function() { + var url = this.map.url; + + //Regular dependency. + if (!urlFetched[url]) { + urlFetched[url] = true; + context.load(this.map.id, url); + } + }, + + /** + * Checks is the module is ready to define itself, and if so, + * define it. If the silent argument is true, then it will just + * define, but not notify listeners, and not ask for a context-wide + * check of all loaded modules. That is useful for cycle breaking. + */ + check: function (silent) { + if (!this.enabled || this.enabling) { + return; + } + + var id = this.map.id, + depExports = this.depExports, + exports = this.exports, + factory = this.factory, + err, cjsModule; + + if (!this.inited) { + this.fetch(); + } else if (this.error) { + this.emit('error', this.error); + } else if (!this.defining) { + //The factory could trigger another require call + //that would result in checking this module to + //define itself again. If already in the process + //of doing that, skip this work. + this.defining = true; + + if (this.depCount < 1 && !this.defined) { + if (isFunction(factory)) { + //If there is an error listener, favor passing + //to that instead of throwing an error. + if (this.events.error) { + try { + exports = context.execCb(id, factory, depExports, exports); + } catch (e) { + err = e; + } + } else { + exports = context.execCb(id, factory, depExports, exports); + } + + if (this.map.isDefine) { + //If setting exports via 'module' is in play, + //favor that over return value and exports. After that, + //favor a non-undefined return value over exports use. + cjsModule = this.module; + if (cjsModule && + cjsModule.exports !== undefined && + //Make sure it is not already the exports value + cjsModule.exports !== this.exports) { + exports = cjsModule.exports; + } else if (exports === undefined && this.usingExports) { + //exports already set the defined value. + exports = this.exports; + } + } + + if (err) { + err.requireMap = this.map; + err.requireModules = [this.map.id]; + err.requireType = 'define'; + return onError((this.error = err)); + } + + } else { + //Just a literal value + exports = factory; + } + + this.exports = exports; + + if (this.map.isDefine && !this.ignore) { + defined[id] = exports; + + if (req.onResourceLoad) { + req.onResourceLoad(context, this.map, this.depMaps); + } + } + + //Clean up + delete registry[id]; + + this.defined = true; + context.waitCount -= 1; + if (context.waitCount === 0) { + //Clear the wait array used for cycles. + waitAry = []; + } + } + + //Finished the define stage. Allow calling check again + //to allow define notifications below in the case of a + //cycle. + this.defining = false; + + if (!silent) { + if (this.defined && !this.defineEmitted) { + this.defineEmitted = true; + this.emit('defined', this.exports); + this.defineEmitComplete = true; + } + } + } + }, + + callPlugin: function() { + var map = this.map, + id = map.id, + pluginMap = makeModuleMap(map.prefix, null, false, true); + + on(pluginMap, 'defined', bind(this, function (plugin) { + var name = this.map.name, + parentName = this.map.parentMap ? this.map.parentMap.name : null, + load, normalizedMap, normalizedMod; + + //If current map is not normalized, wait for that + //normalized name to load instead of continuing. + if (this.map.unnormalized) { + //Normalize the ID if the plugin allows it. + if (plugin.normalize) { + name = plugin.normalize(name, function (name) { + return normalize(name, parentName, true); + }) || ''; + } + + normalizedMap = makeModuleMap(map.prefix + '!' + name, + this.map.parentMap, + false, + true); + on(normalizedMap, + 'defined', bind(this, function (value) { + this.init([], function () { return value; }, null, { + enabled: true, + ignore: true + }); + })); + normalizedMod = registry[normalizedMap.id]; + if (normalizedMod) { + if (this.events.error) { + normalizedMod.on('error', bind(this, function (err) { + this.emit('error', err); + })); + } + normalizedMod.enable(); + } + + return; + } + + load = bind(this, function (value) { + this.init([], function () { return value; }, null, { + enabled: true + }); + }); + + load.error = bind(this, function (err) { + this.inited = true; + this.error = err; + err.requireModules = [id]; + + //Remove temp unnormalized modules for this module, + //since they will never be resolved otherwise now. + eachProp(registry, function (mod) { + if (mod.map.id.indexOf(id + '_unnormalized') === 0) { + removeWaiting(mod.map.id); + } + }); + + onError(err); + }); + + //Allow plugins to load other code without having to know the + //context or how to 'complete' the load. + load.fromText = function (moduleName, text) { + /*jslint evil: true */ + var hasInteractive = useInteractive; + + //Turn off interactive script matching for IE for any define + //calls in the text, then turn it back on at the end. + if (hasInteractive) { + useInteractive = false; + } + + //Prime the system by creating a module instance for + //it. + getModule(makeModuleMap(moduleName)); + + req.exec(text); + + if (hasInteractive) { + useInteractive = true; + } + + //Support anonymous modules. + context.completeLoad(moduleName); + }; + + //Use parentName here since the plugin's name is not reliable, + //could be some weird string with no path that actually wants to + //reference the parentName's path. + plugin.load(map.name, makeRequire(map.parentMap, true, function (deps, cb) { + deps.rjsSkipMap = true; + return context.require(deps, cb); + }), load, config); + })); + + context.enable(pluginMap, this); + this.pluginMaps[pluginMap.id] = pluginMap; + }, + + enable: function () { + this.enabled = true; + + if (!this.waitPushed) { + waitAry.push(this); + context.waitCount += 1; + this.waitPushed = true; + } + + //Set flag mentioning that the module is enabling, + //so that immediate calls to the defined callbacks + //for dependencies do not trigger inadvertent load + //with the depCount still being zero. + this.enabling = true; + + //Enable each dependency + each(this.depMaps, bind(this, function (depMap, i) { + var id, mod, handler; + + if (typeof depMap === 'string') { + //Dependency needs to be converted to a depMap + //and wired up to this module. + depMap = makeModuleMap(depMap, + (this.map.isDefine ? this.map : this.map.parentMap), + false, + !this.depMaps.rjsSkipMap); + this.depMaps[i] = depMap; + + handler = handlers[depMap.id]; + + if (handler) { + this.depExports[i] = handler(this); + return; + } + + this.depCount += 1; + + on(depMap, 'defined', bind(this, function (depExports) { + this.defineDep(i, depExports); + this.check(); + })); + + if (this.errback) { + on(depMap, 'error', this.errback); + } + } + + id = depMap.id; + mod = registry[id]; + + //Skip special modules like 'require', 'exports', 'module' + //Also, don't call enable if it is already enabled, + //important in circular dependency cases. + if (!handlers[id] && mod && !mod.enabled) { + context.enable(depMap, this); + } + })); + + //Enable each plugin that is used in + //a dependency + eachProp(this.pluginMaps, bind(this, function (pluginMap) { + var mod = registry[pluginMap.id]; + if (mod && !mod.enabled) { + context.enable(pluginMap, this); + } + })); + + this.enabling = false; + + this.check(); + }, + + on: function(name, cb) { + var cbs = this.events[name]; + if (!cbs) { + cbs = this.events[name] = []; + } + cbs.push(cb); + }, + + emit: function (name, evt) { + each(this.events[name], function (cb) { + cb(evt); + }); + if (name === 'error') { + //Now that the error handler was triggered, remove + //the listeners, since this broken Module instance + //can stay around for a while in the registry/waitAry. + delete this.events[name]; + } + } + }; + + function callGetModule(args) { + getModule(makeModuleMap(args[0], null, true)).init(args[1], args[2]); + } + + function removeListener(node, func, name, ieName) { + //Favor detachEvent because of IE9 + //issue, see attachEvent/addEventListener comment elsewhere + //in this file. + if (node.detachEvent && !isOpera) { + //Probably IE. If not it will throw an error, which will be + //useful to know. + if (ieName) { + node.detachEvent(ieName, func); + } + } else { + node.removeEventListener(name, func, false); + } + } + + /** + * Given an event from a script node, get the requirejs info from it, + * and then removes the event listeners on the node. + * @param {Event} evt + * @returns {Object} + */ + function getScriptData(evt) { + //Using currentTarget instead of target for Firefox 2.0's sake. Not + //all old browsers will be supported, but this one was easy enough + //to support and still makes sense. + var node = evt.currentTarget || evt.srcElement; + + //Remove the listeners once here. + removeListener(node, context.onScriptLoad, 'load', 'onreadystatechange'); + removeListener(node, context.onScriptError, 'error'); + + return { + node: node, + id: node && node.getAttribute('data-requiremodule') + }; + } + + return (context = { + config: config, + contextName: contextName, + registry: registry, + defined: defined, + urlFetched: urlFetched, + waitCount: 0, + defQueue: defQueue, + Module: Module, + makeModuleMap: makeModuleMap, + + /** + * Set a configuration for the context. + * @param {Object} cfg config object to integrate. + */ + configure: function (cfg) { + //Make sure the baseUrl ends in a slash. + if (cfg.baseUrl) { + if (cfg.baseUrl.charAt(cfg.baseUrl.length - 1) !== '/') { + cfg.baseUrl += '/'; + } + } + + //Save off the paths and packages since they require special processing, + //they are additive. + var pkgs = config.pkgs, + shim = config.shim, + paths = config.paths, + map = config.map; + + //Mix in the config values, favoring the new values over + //existing ones in context.config. + mixin(config, cfg, true); + + //Merge paths. + config.paths = mixin(paths, cfg.paths, true); + + //Merge map + if (cfg.map) { + config.map = mixin(map || {}, cfg.map, true, true); + } + + //Merge shim + if (cfg.shim) { + eachProp(cfg.shim, function (value, id) { + //Normalize the structure + if (isArray(value)) { + value = { + deps: value + }; + } + if (value.exports && !value.exports.__buildReady) { + value.exports = context.makeShimExports(value.exports); + } + shim[id] = value; + }); + config.shim = shim; + } + + //Adjust packages if necessary. + if (cfg.packages) { + each(cfg.packages, function (pkgObj) { + var location; + + pkgObj = typeof pkgObj === 'string' ? { name: pkgObj } : pkgObj; + location = pkgObj.location; + + //Create a brand new object on pkgs, since currentPackages can + //be passed in again, and config.pkgs is the internal transformed + //state for all package configs. + pkgs[pkgObj.name] = { + name: pkgObj.name, + location: location || pkgObj.name, + //Remove leading dot in main, so main paths are normalized, + //and remove any trailing .js, since different package + //envs have different conventions: some use a module name, + //some use a file name. + main: (pkgObj.main || 'main') + .replace(currDirRegExp, '') + .replace(jsSuffixRegExp, '') + }; + }); + + //Done with modifications, assing packages back to context config + config.pkgs = pkgs; + } + + //If there are any "waiting to execute" modules in the registry, + //update the maps for them, since their info, like URLs to load, + //may have changed. + eachProp(registry, function (mod, id) { + mod.map = makeModuleMap(id); + }); + + //If a deps array or a config callback is specified, then call + //require with those args. This is useful when require is defined as a + //config object before require.js is loaded. + if (cfg.deps || cfg.callback) { + context.require(cfg.deps || [], cfg.callback); + } + }, + + makeShimExports: function (exports) { + var func; + if (typeof exports === 'string') { + func = function () { + return getGlobal(exports); + }; + //Save the exports for use in nodefine checking. + func.exports = exports; + return func; + } else { + return function () { + return exports.apply(global, arguments); + }; + } + }, + + requireDefined: function (id, relMap) { + return hasProp(defined, makeModuleMap(id, relMap, false, true).id); + }, + + requireSpecified: function (id, relMap) { + id = makeModuleMap(id, relMap, false, true).id; + return hasProp(defined, id) || hasProp(registry, id); + }, + + require: function (deps, callback, errback, relMap) { + var moduleName, id, map, requireMod, args; + if (typeof deps === 'string') { + if (isFunction(callback)) { + //Invalid call + return onError(makeError('requireargs', 'Invalid require call'), errback); + } + + //Synchronous access to one module. If require.get is + //available (as in the Node adapter), prefer that. + //In this case deps is the moduleName and callback is + //the relMap + if (req.get) { + return req.get(context, deps, callback); + } + + //Just return the module wanted. In this scenario, the + //second arg (if passed) is just the relMap. + moduleName = deps; + relMap = callback; + + //Normalize module name, if it contains . or .. + map = makeModuleMap(moduleName, relMap, false, true); + id = map.id; + + if (!hasProp(defined, id)) { + return onError(makeError('notloaded', 'Module name "' + + id + + '" has not been loaded yet for context: ' + + contextName)); + } + return defined[id]; + } + + //Callback require. Normalize args. if callback or errback is + //not a function, it means it is a relMap. Test errback first. + if (errback && !isFunction(errback)) { + relMap = errback; + errback = undefined; + } + if (callback && !isFunction(callback)) { + relMap = callback; + callback = undefined; + } + + //Any defined modules in the global queue, intake them now. + takeGlobalQueue(); + + //Make sure any remaining defQueue items get properly processed. + while (defQueue.length) { + args = defQueue.shift(); + if (args[0] === null) { + return onError(makeError('mismatch', 'Mismatched anonymous define() module: ' + args[args.length - 1])); + } else { + //args are id, deps, factory. Should be normalized by the + //define() function. + callGetModule(args); + } + } + + //Mark all the dependencies as needing to be loaded. + requireMod = getModule(makeModuleMap(null, relMap)); + + requireMod.init(deps, callback, errback, { + enabled: true + }); + + checkLoaded(); + + return context.require; + }, + + undef: function (id) { + var map = makeModuleMap(id, null, true), + mod = registry[id]; + + delete defined[id]; + delete urlFetched[map.url]; + delete undefEvents[id]; + + if (mod) { + //Hold on to listeners in case the + //module will be attempted to be reloaded + //using a different config. + if (mod.events.defined) { + undefEvents[id] = mod.events; + } + + removeWaiting(id); + } + }, + + /** + * Called to enable a module if it is still in the registry + * awaiting enablement. parent module is passed in for context, + * used by the optimizer. + */ + enable: function (depMap, parent) { + var mod = registry[depMap.id]; + if (mod) { + getModule(depMap).enable(); + } + }, + + /** + * Internal method used by environment adapters to complete a load event. + * A load event could be a script load or just a load pass from a synchronous + * load call. + * @param {String} moduleName the name of the module to potentially complete. + */ + completeLoad: function (moduleName) { + var shim = config.shim[moduleName] || {}, + shExports = shim.exports && shim.exports.exports, + found, args, mod; + + takeGlobalQueue(); + + while (defQueue.length) { + args = defQueue.shift(); + if (args[0] === null) { + args[0] = moduleName; + //If already found an anonymous module and bound it + //to this name, then this is some other anon module + //waiting for its completeLoad to fire. + if (found) { + break; + } + found = true; + } else if (args[0] === moduleName) { + //Found matching define call for this script! + found = true; + } + + callGetModule(args); + } + + //Do this after the cycle of callGetModule in case the result + //of those calls/init calls changes the registry. + mod = registry[moduleName]; + + if (!found && + !defined[moduleName] && + mod && !mod.inited) { + if (config.enforceDefine && (!shExports || !getGlobal(shExports))) { + if (hasPathFallback(moduleName)) { + return; + } else { + return onError(makeError('nodefine', + 'No define call for ' + moduleName, + null, + [moduleName])); + } + } else { + //A script that does not call define(), so just simulate + //the call for it. + callGetModule([moduleName, (shim.deps || []), shim.exports]); + } + } + + checkLoaded(); + }, + + /** + * Converts a module name + .extension into an URL path. + * *Requires* the use of a module name. It does not support using + * plain URLs like nameToUrl. + */ + toUrl: function (moduleNamePlusExt, relModuleMap) { + var index = moduleNamePlusExt.lastIndexOf('.'), + ext = null; + + if (index !== -1) { + ext = moduleNamePlusExt.substring(index, moduleNamePlusExt.length); + moduleNamePlusExt = moduleNamePlusExt.substring(0, index); + } + + return context.nameToUrl(moduleNamePlusExt, ext, relModuleMap); + }, + + /** + * Converts a module name to a file path. Supports cases where + * moduleName may actually be just an URL. + */ + nameToUrl: function (moduleName, ext, relModuleMap) { + var paths, pkgs, pkg, pkgPath, syms, i, parentModule, url, + parentPath; + + //Normalize module name if have a base relative module name to work from. + moduleName = normalize(moduleName, relModuleMap && relModuleMap.id, true); + + //If a colon is in the URL, it indicates a protocol is used and it is just + //an URL to a file, or if it starts with a slash, contains a query arg (i.e. ?) + //or ends with .js, then assume the user meant to use an url and not a module id. + //The slash is important for protocol-less URLs as well as full paths. + if (req.jsExtRegExp.test(moduleName)) { + //Just a plain path, not module name lookup, so just return it. + //Add extension if it is included. This is a bit wonky, only non-.js things pass + //an extension, this method probably needs to be reworked. + url = moduleName + (ext || ''); + } else { + //A module that needs to be converted to a path. + paths = config.paths; + pkgs = config.pkgs; + + syms = moduleName.split('/'); + //For each module name segment, see if there is a path + //registered for it. Start with most specific name + //and work up from it. + for (i = syms.length; i > 0; i -= 1) { + parentModule = syms.slice(0, i).join('/'); + pkg = pkgs[parentModule]; + parentPath = paths[parentModule]; + if (parentPath) { + //If an array, it means there are a few choices, + //Choose the one that is desired + if (isArray(parentPath)) { + parentPath = parentPath[0]; + } + syms.splice(0, i, parentPath); + break; + } else if (pkg) { + //If module name is just the package name, then looking + //for the main module. + if (moduleName === pkg.name) { + pkgPath = pkg.location + '/' + pkg.main; + } else { + pkgPath = pkg.location; + } + syms.splice(0, i, pkgPath); + break; + } + } + + //Join the path parts together, then figure out if baseUrl is needed. + url = syms.join('/') + (ext || '.js'); + url = (url.charAt(0) === '/' || url.match(/^[\w\+\.\-]+:/) ? '' : config.baseUrl) + url; + } + + return config.urlArgs ? url + + ((url.indexOf('?') === -1 ? '?' : '&') + + config.urlArgs) : url; + }, + + //Delegates to req.load. Broken out as a separate function to + //allow overriding in the optimizer. + load: function (id, url) { + req.load(context, id, url); + }, + + /** + * Executes a module callack function. Broken out as a separate function + * solely to allow the build system to sequence the files in the built + * layer in the right sequence. + * + * @private + */ + execCb: function (name, callback, args, exports) { + return callback.apply(exports, args); + }, + + /** + * callback for script loads, used to check status of loading. + * + * @param {Event} evt the event from the browser for the script + * that was loaded. + */ + onScriptLoad: function (evt) { + //Using currentTarget instead of target for Firefox 2.0's sake. Not + //all old browsers will be supported, but this one was easy enough + //to support and still makes sense. + if (evt.type === 'load' || + (readyRegExp.test((evt.currentTarget || evt.srcElement).readyState))) { + //Reset interactive script so a script node is not held onto for + //to long. + interactiveScript = null; + + //Pull out the name of the module and the context. + var data = getScriptData(evt); + context.completeLoad(data.id); + } + }, + + /** + * Callback for script errors. + */ + onScriptError: function (evt) { + var data = getScriptData(evt); + if (!hasPathFallback(data.id)) { + return onError(makeError('scripterror', 'Script error', evt, [data.id])); + } + } + }); + } + + /** + * Main entry point. + * + * If the only argument to require is a string, then the module that + * is represented by that string is fetched for the appropriate context. + * + * If the first argument is an array, then it will be treated as an array + * of dependency string names to fetch. An optional function callback can + * be specified to execute when all of those dependencies are available. + * + * Make a local req variable to help Caja compliance (it assumes things + * on a require that are not standardized), and to give a short + * name for minification/local scope use. + */ + req = requirejs = function (deps, callback, errback, optional) { + + //Find the right context, use default + var contextName = defContextName, + context, config; + + // Determine if have config object in the call. + if (!isArray(deps) && typeof deps !== 'string') { + // deps is a config object + config = deps; + if (isArray(callback)) { + // Adjust args if there are dependencies + deps = callback; + callback = errback; + errback = optional; + } else { + deps = []; + } + } + + if (config && config.context) { + contextName = config.context; + } + + context = contexts[contextName]; + if (!context) { + context = contexts[contextName] = req.s.newContext(contextName); + } + + if (config) { + context.configure(config); + } + + return context.require(deps, callback, errback); + }; + + /** + * Support require.config() to make it easier to cooperate with other + * AMD loaders on globally agreed names. + */ + req.config = function (config) { + return req(config); + }; + + /** + * Export require as a global, but only if it does not already exist. + */ + if (!require) { + require = req; + } + + req.version = version; + + //Used to filter out dependencies that are already paths. + req.jsExtRegExp = /^\/|:|\?|\.js$/; + req.isBrowser = isBrowser; + s = req.s = { + contexts: contexts, + newContext: newContext + }; + + //Create default context. + req({}); + + //Exports some context-sensitive methods on global require, using + //default context if no context specified. + addRequireMethods(req); + + if (isBrowser) { + head = s.head = document.getElementsByTagName('head')[0]; + //If BASE tag is in play, using appendChild is a problem for IE6. + //When that browser dies, this can be removed. Details in this jQuery bug: + //http://dev.jquery.com/ticket/2709 + baseElement = document.getElementsByTagName('base')[0]; + if (baseElement) { + head = s.head = baseElement.parentNode; + } + } + + /** + * Any errors that require explicitly generates will be passed to this + * function. Intercept/override it if you want custom error handling. + * @param {Error} err the error object. + */ + req.onError = function (err) { + throw err; + }; + + /** + * Does the request to load a module for the browser case. + * Make this a separate function to allow other environments + * to override it. + * + * @param {Object} context the require context to find state. + * @param {String} moduleName the name of the module. + * @param {Object} url the URL to the module. + */ + req.load = function (context, moduleName, url) { + var config = (context && context.config) || {}, + node; + if (isBrowser) { + //In the browser so use a script tag + node = config.xhtml ? + document.createElementNS('http://www.w3.org/1999/xhtml', 'html:script') : + document.createElement('script'); + node.type = config.scriptType || 'text/javascript'; + node.charset = 'utf-8'; + + node.setAttribute('data-requirecontext', context.contextName); + node.setAttribute('data-requiremodule', moduleName); + + //Set up load listener. Test attachEvent first because IE9 has + //a subtle issue in its addEventListener and script onload firings + //that do not match the behavior of all other browsers with + //addEventListener support, which fire the onload event for a + //script right after the script execution. See: + //https://connect.microsoft.com/IE/feedback/details/648057/script-onload-event-is-not-fired-immediately-after-script-execution + //UNFORTUNATELY Opera implements attachEvent but does not follow the script + //script execution mode. + if (node.attachEvent && + //Check if node.attachEvent is artificially added by custom script or + //natively supported by browser + //read https://github.com/jrburke/requirejs/issues/187 + //if we can NOT find [native code] then it must NOT natively supported. + //in IE8, node.attachEvent does not have toString() + //Note the test for "[native code" with no closing brace, see: + //https://github.com/jrburke/requirejs/issues/273 + !(node.attachEvent.toString && node.attachEvent.toString().indexOf('[native code') < 0) && + !isOpera) { + //Probably IE. IE (at least 6-8) do not fire + //script onload right after executing the script, so + //we cannot tie the anonymous define call to a name. + //However, IE reports the script as being in 'interactive' + //readyState at the time of the define call. + useInteractive = true; + + node.attachEvent('onreadystatechange', context.onScriptLoad); + //It would be great to add an error handler here to catch + //404s in IE9+. However, onreadystatechange will fire before + //the error handler, so that does not help. If addEvenListener + //is used, then IE will fire error before load, but we cannot + //use that pathway given the connect.microsoft.com issue + //mentioned above about not doing the 'script execute, + //then fire the script load event listener before execute + //next script' that other browsers do. + //Best hope: IE10 fixes the issues, + //and then destroys all installs of IE 6-9. + //node.attachEvent('onerror', context.onScriptError); + } else { + node.addEventListener('load', context.onScriptLoad, false); + node.addEventListener('error', context.onScriptError, false); + } + node.src = url; + + //For some cache cases in IE 6-8, the script executes before the end + //of the appendChild execution, so to tie an anonymous define + //call to the module name (which is stored on the node), hold on + //to a reference to this node, but clear after the DOM insertion. + currentlyAddingScript = node; + if (baseElement) { + head.insertBefore(node, baseElement); + } else { + head.appendChild(node); + } + currentlyAddingScript = null; + + return node; + } else if (isWebWorker) { + //In a web worker, use importScripts. This is not a very + //efficient use of importScripts, importScripts will block until + //its script is downloaded and evaluated. However, if web workers + //are in play, the expectation that a build has been done so that + //only one script needs to be loaded anyway. This may need to be + //reevaluated if other use cases become common. + importScripts(url); + + //Account for anonymous modules + context.completeLoad(moduleName); + } + }; + + function getInteractiveScript() { + if (interactiveScript && interactiveScript.readyState === 'interactive') { + return interactiveScript; + } + + eachReverse(scripts(), function (script) { + if (script.readyState === 'interactive') { + return (interactiveScript = script); + } + }); + return interactiveScript; + } + + //Look for a data-main script attribute, which could also adjust the baseUrl. + if (isBrowser) { + //Figure out baseUrl. Get it from the script tag with require.js in it. + eachReverse(scripts(), function (script) { + //Set the 'head' where we can append children by + //using the script's parent. + if (!head) { + head = script.parentNode; + } + + //Look for a data-main attribute to set main script for the page + //to load. If it is there, the path to data main becomes the + //baseUrl, if it is not already set. + dataMain = script.getAttribute('data-main'); + if (dataMain) { + + //Pull off the directory of data-main for use as the + //baseUrl. + src = dataMain.split('/'); + mainScript = src.pop(); + subPath = src.length ? src.join('/') + '/' : './'; + + //Set final baseUrl if there is not already an explicit one. + if (!cfg.baseUrl) { + cfg.baseUrl = subPath; + } + + //Strip off any trailing .js since dataMain is now + //like a module name. + dataMain = mainScript.replace(jsSuffixRegExp, ''); + + //Put the data-main script in the files to load. + cfg.deps = cfg.deps ? cfg.deps.concat(dataMain) : [dataMain]; + + return true; + } + }); + } + + /** + * The function that handles definitions of modules. Differs from + * require() in that a string for the module should be the first argument, + * and the function to execute after dependencies are loaded should + * return a value to define the module corresponding to the first argument's + * name. + */ + define = function (name, deps, callback) { + var node, context; + + //Allow for anonymous functions + if (typeof name !== 'string') { + //Adjust args appropriately + callback = deps; + deps = name; + name = null; + } + + //This module may not have dependencies + if (!isArray(deps)) { + callback = deps; + deps = []; + } + + //If no name, and callback is a function, then figure out if it a + //CommonJS thing with dependencies. + if (!deps.length && isFunction(callback)) { + //Remove comments from the callback string, + //look for require calls, and pull them into the dependencies, + //but only if there are function args. + if (callback.length) { + callback + .toString() + .replace(commentRegExp, '') + .replace(cjsRequireRegExp, function (match, dep) { + deps.push(dep); + }); + + //May be a CommonJS thing even without require calls, but still + //could use exports, and module. Avoid doing exports and module + //work though if it just needs require. + //REQUIRES the function to expect the CommonJS variables in the + //order listed below. + deps = (callback.length === 1 ? ['require'] : ['require', 'exports', 'module']).concat(deps); + } + } + + //If in IE 6-8 and hit an anonymous define() call, do the interactive + //work. + if (useInteractive) { + node = currentlyAddingScript || getInteractiveScript(); + if (node) { + if (!name) { + name = node.getAttribute('data-requiremodule'); + } + context = contexts[node.getAttribute('data-requirecontext')]; + } + } + + //Always save off evaluating the def call until the script onload handler. + //This allows multiple modules to be in a file without prematurely + //tracing dependencies, and allows for anonymous module support, + //where the module name is not known until the script onload event + //occurs. If no context, use the global queue, and get it processed + //in the onscript load callback. + (context ? context.defQueue : globalDefQueue).push([name, deps, callback]); + }; + + define.amd = { + jQuery: true + }; + + + /** + * Executes the text. Normally just uses eval, but can be modified + * to use a better, environment-specific call. Only used for transpiling + * loader plugins, not for plain JS modules. + * @param {String} text the text to execute/evaluate. + */ + req.exec = function (text) { + /*jslint evil: true */ + return eval(text); + }; + + //Set up with config info. + req(cfg); +}(this)); diff --git a/vendor/assets/javascripts/sinon-chai.js b/vendor/assets/javascripts/sinon-chai.js new file mode 100644 index 0000000000..9c940b753c --- /dev/null +++ b/vendor/assets/javascripts/sinon-chai.js @@ -0,0 +1,126 @@ +(function (sinonChai) { + "use strict"; + + // Module systems magic dance. + + if (typeof require === "function" && typeof exports === "object" && typeof module === "object") { + // NodeJS + module.exports = sinonChai; + } else if (typeof define === "function" && define.amd) { + // AMD + define(function () { + return sinonChai; + }); + } else { + // Other environment (usually + + + + + + +

      GFW Unit Tests

      +
      + + + \ No newline at end of file diff --git a/jstest/lib/boot.js b/jstest/lib/boot.js new file mode 100644 index 0000000000..ec8baa0aa5 --- /dev/null +++ b/jstest/lib/boot.js @@ -0,0 +1,181 @@ +/** + Starting with version 2.0, this file "boots" Jasmine, performing all of the necessary initialization before executing the loaded environment and all of a project's specs. This file should be loaded after `jasmine.js`, but before any project source files or spec files are loaded. Thus this file can also be used to customize Jasmine for a project. + + If a project is using Jasmine via the standalone distribution, this file can be customized directly. If a project is using Jasmine via the [Ruby gem][jasmine-gem], this file can be copied into the support directory via `jasmine copy_boot_js`. Other environments (e.g., Python) will have different mechanisms. + + The location of `boot.js` can be specified and/or overridden in `jasmine.yml`. + + [jasmine-gem]: http://github.com/pivotal/jasmine-gem + */ + +(function() { + + /** + * ## Require & Instantiate + * + * Require Jasmine's core files. Specifically, this requires and attaches all of Jasmine's code to the `jasmine` reference. + */ + window.jasmine = jasmineRequire.core(jasmineRequire); + + /** + * Since this is being run in a browser and the results should populate to an HTML page, require the HTML-specific Jasmine code, injecting the same reference. + */ + jasmineRequire.html(jasmine); + + /** + * Create the Jasmine environment. This is used to run all specs in a project. + */ + var env = jasmine.getEnv(); + + /** + * ## The Global Interface + * + * Build up the functions that will be exposed as the Jasmine public interface. A project can customize, rename or alias any of these functions as desired, provided the implementation remains unchanged. + */ + var jasmineInterface = { + describe: function(description, specDefinitions) { + return env.describe(description, specDefinitions); + }, + + xdescribe: function(description, specDefinitions) { + return env.xdescribe(description, specDefinitions); + }, + + it: function(desc, func) { + return env.it(desc, func); + }, + + xit: function(desc, func) { + return env.xit(desc, func); + }, + + beforeEach: function(beforeEachFunction) { + return env.beforeEach(beforeEachFunction); + }, + + afterEach: function(afterEachFunction) { + return env.afterEach(afterEachFunction); + }, + + expect: function(actual) { + return env.expect(actual); + }, + + pending: function() { + return env.pending(); + }, + + spyOn: function(obj, methodName) { + return env.spyOn(obj, methodName); + }, + + jsApiReporter: new jasmine.JsApiReporter({ + timer: new jasmine.Timer() + }) + }; + + /** + * Add all of the Jasmine global/public interface to the proper global, so a project can use the public interface directly. For example, calling `describe` in specs instead of `jasmine.getEnv().describe`. + */ + if (typeof window == "undefined" && typeof exports == "object") { + extend(exports, jasmineInterface); + } else { + extend(window, jasmineInterface); + } + + /** + * Expose the interface for adding custom equality testers. + */ + jasmine.addCustomEqualityTester = function(tester) { + env.addCustomEqualityTester(tester); + }; + + /** + * Expose the interface for adding custom expectation matchers + */ + jasmine.addMatchers = function(matchers) { + return env.addMatchers(matchers); + }; + + /** + * Expose the mock interface for the JavaScript timeout functions + */ + jasmine.clock = function() { + return env.clock; + }; + + /** + * ## Runner Parameters + * + * More browser specific code - wrap the query string in an object and to allow for getting/setting parameters from the runner user interface. + */ + + var queryString = new jasmine.QueryString({ + getWindowLocation: function() { return window.location; } + }); + + var catchingExceptions = queryString.getParam("catch"); + env.catchExceptions(typeof catchingExceptions === "undefined" ? true : catchingExceptions); + + /** + * ## Reporters + * The `HtmlReporter` builds all of the HTML UI for the runner page. This reporter paints the dots, stars, and x's for specs, as well as all spec names and all failures (if any). + */ + var htmlReporter = new jasmine.HtmlReporter({ + env: env, + onRaiseExceptionsClick: function() { queryString.setParam("catch", !env.catchingExceptions()); }, + getContainer: function() { return document.body; }, + createElement: function() { return document.createElement.apply(document, arguments); }, + createTextNode: function() { return document.createTextNode.apply(document, arguments); }, + timer: new jasmine.Timer() + }); + + /** + * The `jsApiReporter` also receives spec results, and is used by any environment that needs to extract the results from JavaScript. + */ + env.addReporter(jasmineInterface.jsApiReporter); + env.addReporter(htmlReporter); + + /** + * Filter which specs will be run by matching the start of the full name against the `spec` query param. + */ + var specFilter = new jasmine.HtmlSpecFilter({ + filterString: function() { return queryString.getParam("spec"); } + }); + + env.specFilter = function(spec) { + return specFilter.matches(spec.getFullName()); + }; + + /** + * Setting up timing functions to be able to be overridden. Certain browsers (Safari, IE 8, phantomjs) require this hack. + */ + window.setTimeout = window.setTimeout; + window.setInterval = window.setInterval; + window.clearTimeout = window.clearTimeout; + window.clearInterval = window.clearInterval; + + /** + * ## Execution + * + * Replace the browser window's `onload`, ensure it's called, and then run all of the loaded specs. This includes initializing the `HtmlReporter` instance and then executing the loaded Jasmine environment. All of this will happen after all of the specs are loaded. + */ + var currentWindowOnload = window.onload; + + window.onload = function() { + if (currentWindowOnload) { + currentWindowOnload(); + } + htmlReporter.initialize(); + env.execute(); + }; + + /** + * Helper function for readability above. + */ + function extend(destination, source) { + for (var property in source) destination[property] = source[property]; + return destination; + } + +}()); diff --git a/jstest/lib/jasmine-html.js b/jstest/lib/jasmine-html.js new file mode 100644 index 0000000000..985d0d1a0a --- /dev/null +++ b/jstest/lib/jasmine-html.js @@ -0,0 +1,359 @@ +/* +Copyright (c) 2008-2013 Pivotal Labs + +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. +*/ +jasmineRequire.html = function(j$) { + j$.ResultsNode = jasmineRequire.ResultsNode(); + j$.HtmlReporter = jasmineRequire.HtmlReporter(j$); + j$.QueryString = jasmineRequire.QueryString(); + j$.HtmlSpecFilter = jasmineRequire.HtmlSpecFilter(); +}; + +jasmineRequire.HtmlReporter = function(j$) { + + var noopTimer = { + start: function() {}, + elapsed: function() { return 0; } + }; + + function HtmlReporter(options) { + var env = options.env || {}, + getContainer = options.getContainer, + createElement = options.createElement, + createTextNode = options.createTextNode, + onRaiseExceptionsClick = options.onRaiseExceptionsClick || function() {}, + timer = options.timer || noopTimer, + results = [], + specsExecuted = 0, + failureCount = 0, + pendingSpecCount = 0, + htmlReporterMain, + symbols; + + this.initialize = function() { + htmlReporterMain = createDom("div", {className: "html-reporter"}, + createDom("div", {className: "banner"}, + createDom("span", {className: "title"}, "Jasmine"), + createDom("span", {className: "version"}, j$.version) + ), + createDom("ul", {className: "symbol-summary"}), + createDom("div", {className: "alert"}), + createDom("div", {className: "results"}, + createDom("div", {className: "failures"}) + ) + ); + getContainer().appendChild(htmlReporterMain); + + symbols = find(".symbol-summary"); + }; + + var totalSpecsDefined; + this.jasmineStarted = function(options) { + totalSpecsDefined = options.totalSpecsDefined || 0; + timer.start(); + }; + + var summary = createDom("div", {className: "summary"}); + + var topResults = new j$.ResultsNode({}, "", null), + currentParent = topResults; + + this.suiteStarted = function(result) { + currentParent.addChild(result, "suite"); + currentParent = currentParent.last(); + }; + + this.suiteDone = function(result) { + if (currentParent == topResults) { + return; + } + + currentParent = currentParent.parent; + }; + + this.specStarted = function(result) { + currentParent.addChild(result, "spec"); + }; + + var failures = []; + this.specDone = function(result) { + if (result.status != "disabled") { + specsExecuted++; + } + + symbols.appendChild(createDom("li", { + className: result.status, + id: "spec_" + result.id, + title: result.fullName + } + )); + + if (result.status == "failed") { + failureCount++; + + var failure = + createDom("div", {className: "spec-detail failed"}, + createDom("div", {className: "description"}, + createDom("a", {title: result.fullName, href: specHref(result)}, result.fullName) + ), + createDom("div", {className: "messages"}) + ); + var messages = failure.childNodes[1]; + + for (var i = 0; i < result.failedExpectations.length; i++) { + var expectation = result.failedExpectations[i]; + messages.appendChild(createDom("div", {className: "result-message"}, expectation.message)); + messages.appendChild(createDom("div", {className: "stack-trace"}, expectation.stack)); + } + + failures.push(failure); + } + + if (result.status == "pending") { + pendingSpecCount++; + } + }; + + this.jasmineDone = function() { + var banner = find(".banner"); + banner.appendChild(createDom("span", {className: "duration"}, "finished in " + timer.elapsed() / 1000 + "s")); + + var alert = find(".alert"); + + alert.appendChild(createDom("span", { className: "exceptions" }, + createDom("label", { className: "label", 'for': "raise-exceptions" }, "raise exceptions"), + createDom("input", { + className: "raise", + id: "raise-exceptions", + type: "checkbox" + }) + )); + var checkbox = find("input"); + + checkbox.checked = !env.catchingExceptions(); + checkbox.onclick = onRaiseExceptionsClick; + + if (specsExecuted < totalSpecsDefined) { + var skippedMessage = "Ran " + specsExecuted + " of " + totalSpecsDefined + " specs - run all"; + alert.appendChild( + createDom("span", {className: "bar skipped"}, + createDom("a", {href: "?", title: "Run all specs"}, skippedMessage) + ) + ); + } + var statusBarMessage = "" + pluralize("spec", specsExecuted) + ", " + pluralize("failure", failureCount); + if (pendingSpecCount) { statusBarMessage += ", " + pluralize("pending spec", pendingSpecCount); } + + var statusBarClassName = "bar " + ((failureCount > 0) ? "failed" : "passed"); + alert.appendChild(createDom("span", {className: statusBarClassName}, statusBarMessage)); + + var results = find(".results"); + results.appendChild(summary); + + summaryList(topResults, summary); + + function summaryList(resultsTree, domParent) { + var specListNode; + for (var i = 0; i < resultsTree.children.length; i++) { + var resultNode = resultsTree.children[i]; + if (resultNode.type == "suite") { + var suiteListNode = createDom("ul", {className: "suite", id: "suite-" + resultNode.result.id}, + createDom("li", {className: "suite-detail"}, + createDom("a", {href: specHref(resultNode.result)}, resultNode.result.description) + ) + ); + + summaryList(resultNode, suiteListNode); + domParent.appendChild(suiteListNode); + } + if (resultNode.type == "spec") { + if (domParent.getAttribute("class") != "specs") { + specListNode = createDom("ul", {className: "specs"}); + domParent.appendChild(specListNode); + } + specListNode.appendChild( + createDom("li", { + className: resultNode.result.status, + id: "spec-" + resultNode.result.id + }, + createDom("a", {href: specHref(resultNode.result)}, resultNode.result.description) + ) + ); + } + } + } + + if (failures.length) { + alert.appendChild( + createDom('span', {className: "menu bar spec-list"}, + createDom("span", {}, "Spec List | "), + createDom('a', {className: "failures-menu", href: "#"}, "Failures"))); + alert.appendChild( + createDom('span', {className: "menu bar failure-list"}, + createDom('a', {className: "spec-list-menu", href: "#"}, "Spec List"), + createDom("span", {}, " | Failures "))); + + find(".failures-menu").onclick = function() { + setMenuModeTo('failure-list'); + }; + find(".spec-list-menu").onclick = function() { + setMenuModeTo('spec-list'); + }; + + setMenuModeTo('failure-list'); + + var failureNode = find(".failures"); + for (var i = 0; i < failures.length; i++) { + failureNode.appendChild(failures[i]); + } + } + }; + + return this; + + function find(selector) { + return getContainer().querySelector(selector); + } + + function createDom(type, attrs, childrenVarArgs) { + var el = createElement(type); + + for (var i = 2; i < arguments.length; i++) { + var child = arguments[i]; + + if (typeof child === 'string') { + el.appendChild(createTextNode(child)); + } else { + if (child) { + el.appendChild(child); + } + } + } + + for (var attr in attrs) { + if (attr == "className") { + el[attr] = attrs[attr]; + } else { + el.setAttribute(attr, attrs[attr]); + } + } + + return el; + } + + function pluralize(singular, count) { + var word = (count == 1 ? singular : singular + "s"); + + return "" + count + " " + word; + } + + function specHref(result) { + return "?spec=" + encodeURIComponent(result.fullName); + } + + function setMenuModeTo(mode) { + htmlReporterMain.setAttribute("class", "html-reporter " + mode); + } + } + + return HtmlReporter; +}; + +jasmineRequire.HtmlSpecFilter = function() { + function HtmlSpecFilter(options) { + var filterString = options && options.filterString() && options.filterString().replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&"); + var filterPattern = new RegExp(filterString); + + this.matches = function(specName) { + return filterPattern.test(specName); + }; + } + + return HtmlSpecFilter; +}; + +jasmineRequire.ResultsNode = function() { + function ResultsNode(result, type, parent) { + this.result = result; + this.type = type; + this.parent = parent; + + this.children = []; + + this.addChild = function(result, type) { + this.children.push(new ResultsNode(result, type, this)); + }; + + this.last = function() { + return this.children[this.children.length - 1]; + }; + } + + return ResultsNode; +}; + +jasmineRequire.QueryString = function() { + function QueryString(options) { + + this.setParam = function(key, value) { + var paramMap = queryStringToParamMap(); + paramMap[key] = value; + options.getWindowLocation().search = toQueryString(paramMap); + }; + + this.getParam = function(key) { + return queryStringToParamMap()[key]; + }; + + return this; + + function toQueryString(paramMap) { + var qStrPairs = []; + for (var prop in paramMap) { + qStrPairs.push(encodeURIComponent(prop) + "=" + encodeURIComponent(paramMap[prop])); + } + return "?" + qStrPairs.join('&'); + } + + function queryStringToParamMap() { + var paramStr = options.getWindowLocation().search.substring(1), + params = [], + paramMap = {}; + + if (paramStr.length > 0) { + params = paramStr.split('&'); + for (var i = 0; i < params.length; i++) { + var p = params[i].split('='); + var value = decodeURIComponent(p[1]); + if (value === "true" || value === "false") { + value = JSON.parse(value); + } + paramMap[decodeURIComponent(p[0])] = value; + } + } + + return paramMap; + } + + } + + return QueryString; +}; diff --git a/jstest/lib/jasmine.css b/jstest/lib/jasmine.css new file mode 100644 index 0000000000..f4d35b6e78 --- /dev/null +++ b/jstest/lib/jasmine.css @@ -0,0 +1,55 @@ +body { background-color: #eeeeee; padding: 0; margin: 5px; overflow-y: scroll; } + +.html-reporter { font-size: 11px; font-family: Monaco, "Lucida Console", monospace; line-height: 14px; color: #333333; } +.html-reporter a { text-decoration: none; } +.html-reporter a:hover { text-decoration: underline; } +.html-reporter p, .html-reporter h1, .html-reporter h2, .html-reporter h3, .html-reporter h4, .html-reporter h5, .html-reporter h6 { margin: 0; line-height: 14px; } +.html-reporter .banner, .html-reporter .symbol-summary, .html-reporter .summary, .html-reporter .result-message, .html-reporter .spec .description, .html-reporter .spec-detail .description, .html-reporter .alert .bar, .html-reporter .stack-trace { padding-left: 9px; padding-right: 9px; } +.html-reporter .banner .version { margin-left: 14px; } +.html-reporter #jasmine_content { position: fixed; right: 100%; } +.html-reporter .version { color: #aaaaaa; } +.html-reporter .banner { margin-top: 14px; } +.html-reporter .duration { color: #aaaaaa; float: right; } +.html-reporter .symbol-summary { overflow: hidden; *zoom: 1; margin: 14px 0; } +.html-reporter .symbol-summary li { display: inline-block; height: 8px; width: 14px; font-size: 16px; } +.html-reporter .symbol-summary li.passed { font-size: 14px; } +.html-reporter .symbol-summary li.passed:before { color: #5e7d00; content: "\02022"; } +.html-reporter .symbol-summary li.failed { line-height: 9px; } +.html-reporter .symbol-summary li.failed:before { color: #b03911; content: "x"; font-weight: bold; margin-left: -1px; } +.html-reporter .symbol-summary li.disabled { font-size: 14px; } +.html-reporter .symbol-summary li.disabled:before { color: #bababa; content: "\02022"; } +.html-reporter .symbol-summary li.pending { line-height: 17px; } +.html-reporter .symbol-summary li.pending:before { color: #ba9d37; content: "*"; } +.html-reporter .exceptions { color: #fff; float: right; margin-top: 5px; margin-right: 5px; } +.html-reporter .bar { line-height: 28px; font-size: 14px; display: block; color: #eee; } +.html-reporter .bar.failed { background-color: #b03911; } +.html-reporter .bar.passed { background-color: #a6b779; } +.html-reporter .bar.skipped { background-color: #bababa; } +.html-reporter .bar.menu { background-color: #fff; color: #aaaaaa; } +.html-reporter .bar.menu a { color: #333333; } +.html-reporter .bar a { color: white; } +.html-reporter.spec-list .bar.menu.failure-list, .html-reporter.spec-list .results .failures { display: none; } +.html-reporter.failure-list .bar.menu.spec-list, .html-reporter.failure-list .summary { display: none; } +.html-reporter .running-alert { background-color: #666666; } +.html-reporter .results { margin-top: 14px; } +.html-reporter.showDetails .summaryMenuItem { font-weight: normal; text-decoration: inherit; } +.html-reporter.showDetails .summaryMenuItem:hover { text-decoration: underline; } +.html-reporter.showDetails .detailsMenuItem { font-weight: bold; text-decoration: underline; } +.html-reporter.showDetails .summary { display: none; } +.html-reporter.showDetails #details { display: block; } +.html-reporter .summaryMenuItem { font-weight: bold; text-decoration: underline; } +.html-reporter .summary { margin-top: 14px; } +.html-reporter .summary ul { list-style-type: none; margin-left: 14px; padding-top: 0; padding-left: 0; } +.html-reporter .summary ul.suite { margin-top: 7px; margin-bottom: 7px; } +.html-reporter .summary li.passed a { color: #5e7d00; } +.html-reporter .summary li.failed a { color: #b03911; } +.html-reporter .summary li.pending a { color: #ba9d37; } +.html-reporter .description + .suite { margin-top: 0; } +.html-reporter .suite { margin-top: 14px; } +.html-reporter .suite a { color: #333333; } +.html-reporter .failures .spec-detail { margin-bottom: 28px; } +.html-reporter .failures .spec-detail .description { background-color: #b03911; } +.html-reporter .failures .spec-detail .description a { color: white; } +.html-reporter .result-message { padding-top: 14px; color: #333333; white-space: pre; } +.html-reporter .result-message span.result { display: block; } +.html-reporter .stack-trace { margin: 5px 0 0 0; max-height: 224px; overflow: auto; line-height: 18px; color: #666666; border: 1px solid #ddd; background: white; white-space: pre; } diff --git a/jstest/lib/jasmine.js b/jstest/lib/jasmine.js new file mode 100644 index 0000000000..24463ecb83 --- /dev/null +++ b/jstest/lib/jasmine.js @@ -0,0 +1,2402 @@ +/* +Copyright (c) 2008-2013 Pivotal Labs + +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. +*/ +function getJasmineRequireObj() { + if (typeof module !== "undefined" && module.exports) { + return exports; + } else { + window.jasmineRequire = window.jasmineRequire || {}; + return window.jasmineRequire; + } +} + +getJasmineRequireObj().core = function(jRequire) { + var j$ = {}; + + jRequire.base(j$); + j$.util = jRequire.util(); + j$.Any = jRequire.Any(); + j$.CallTracker = jRequire.CallTracker(); + j$.Clock = jRequire.Clock(); + j$.DelayedFunctionScheduler = jRequire.DelayedFunctionScheduler(); + j$.Env = jRequire.Env(j$); + j$.ExceptionFormatter = jRequire.ExceptionFormatter(); + j$.Expectation = jRequire.Expectation(); + j$.buildExpectationResult = jRequire.buildExpectationResult(); + j$.JsApiReporter = jRequire.JsApiReporter(); + j$.matchersUtil = jRequire.matchersUtil(j$); + j$.ObjectContaining = jRequire.ObjectContaining(j$); + j$.pp = jRequire.pp(j$); + j$.QueueRunner = jRequire.QueueRunner(); + j$.ReportDispatcher = jRequire.ReportDispatcher(); + j$.Spec = jRequire.Spec(j$); + j$.SpyStrategy = jRequire.SpyStrategy(); + j$.Suite = jRequire.Suite(); + j$.Timer = jRequire.Timer(); + j$.version = jRequire.version(); + + j$.matchers = jRequire.requireMatchers(jRequire, j$); + + return j$; +}; + +getJasmineRequireObj().requireMatchers = function(jRequire, j$) { + var availableMatchers = [ + "toBe", + "toBeCloseTo", + "toBeDefined", + "toBeFalsy", + "toBeGreaterThan", + "toBeLessThan", + "toBeNaN", + "toBeNull", + "toBeTruthy", + "toBeUndefined", + "toContain", + "toEqual", + "toHaveBeenCalled", + "toHaveBeenCalledWith", + "toMatch", + "toThrow", + "toThrowError" + ], + matchers = {}; + + for (var i = 0; i < availableMatchers.length; i++) { + var name = availableMatchers[i]; + matchers[name] = jRequire[name](j$); + } + + return matchers; +}; + +getJasmineRequireObj().base = function(j$) { + j$.unimplementedMethod_ = function() { + throw new Error("unimplemented method"); + }; + + j$.MAX_PRETTY_PRINT_DEPTH = 40; + j$.DEFAULT_TIMEOUT_INTERVAL = 5000; + + j$.getGlobal = (function() { + var jasmineGlobal = eval.call(null, "this"); + return function() { + return jasmineGlobal; + }; + })(); + + j$.getEnv = function(options) { + var env = j$.currentEnv_ = j$.currentEnv_ || new j$.Env(options); + //jasmine. singletons in here (setTimeout blah blah). + return env; + }; + + j$.isArray_ = function(value) { + return j$.isA_("Array", value); + }; + + j$.isString_ = function(value) { + return j$.isA_("String", value); + }; + + j$.isNumber_ = function(value) { + return j$.isA_("Number", value); + }; + + j$.isA_ = function(typeName, value) { + return Object.prototype.toString.apply(value) === '[object ' + typeName + ']'; + }; + + j$.isDomNode = function(obj) { + return obj.nodeType > 0; + }; + + j$.any = function(clazz) { + return new j$.Any(clazz); + }; + + j$.objectContaining = function(sample) { + return new j$.ObjectContaining(sample); + }; + + j$.createSpy = function(name, originalFn) { + + var spyStrategy = new j$.SpyStrategy({ + name: name, + fn: originalFn, + getSpy: function() { return spy; } + }), + callTracker = new j$.CallTracker(), + spy = function() { + callTracker.track({ + object: this, + args: Array.prototype.slice.apply(arguments) + }); + return spyStrategy.exec.apply(this, arguments); + }; + + for (var prop in originalFn) { + if (prop === 'and' || prop === 'calls') { + throw new Error("Jasmine spies would overwrite the 'and' and 'calls' properties on the object being spied upon"); + } + + spy[prop] = originalFn[prop]; + } + + spy.and = spyStrategy; + spy.calls = callTracker; + + return spy; + }; + + j$.isSpy = function(putativeSpy) { + if (!putativeSpy) { + return false; + } + return putativeSpy.and instanceof j$.SpyStrategy && + putativeSpy.calls instanceof j$.CallTracker; + }; + + j$.createSpyObj = function(baseName, methodNames) { + if (!j$.isArray_(methodNames) || methodNames.length === 0) { + throw "createSpyObj requires a non-empty array of method names to create spies for"; + } + var obj = {}; + for (var i = 0; i < methodNames.length; i++) { + obj[methodNames[i]] = j$.createSpy(baseName + '.' + methodNames[i]); + } + return obj; + }; +}; + +getJasmineRequireObj().util = function() { + + var util = {}; + + util.inherit = function(childClass, parentClass) { + var Subclass = function() { + }; + Subclass.prototype = parentClass.prototype; + childClass.prototype = new Subclass(); + }; + + util.htmlEscape = function(str) { + if (!str) { + return str; + } + return str.replace(/&/g, '&') + .replace(//g, '>'); + }; + + util.argsToArray = function(args) { + var arrayOfArgs = []; + for (var i = 0; i < args.length; i++) { + arrayOfArgs.push(args[i]); + } + return arrayOfArgs; + }; + + util.isUndefined = function(obj) { + return obj === void 0; + }; + + return util; +}; + +getJasmineRequireObj().Spec = function(j$) { + function Spec(attrs) { + this.expectationFactory = attrs.expectationFactory; + this.resultCallback = attrs.resultCallback || function() {}; + this.id = attrs.id; + this.description = attrs.description || ''; + this.fn = attrs.fn; + this.beforeFns = attrs.beforeFns || function() { return []; }; + this.afterFns = attrs.afterFns || function() { return []; }; + this.onStart = attrs.onStart || function() {}; + this.exceptionFormatter = attrs.exceptionFormatter || function() {}; + this.getSpecName = attrs.getSpecName || function() { return ''; }; + this.expectationResultFactory = attrs.expectationResultFactory || function() { }; + this.queueRunnerFactory = attrs.queueRunnerFactory || function() {}; + this.catchingExceptions = attrs.catchingExceptions || function() { return true; }; + + this.timer = attrs.timer || {setTimeout: setTimeout, clearTimeout: clearTimeout}; + + if (!this.fn) { + this.pend(); + } + + this.result = { + id: this.id, + description: this.description, + fullName: this.getFullName(), + failedExpectations: [] + }; + } + + Spec.prototype.addExpectationResult = function(passed, data) { + if (passed) { + return; + } + this.result.failedExpectations.push(this.expectationResultFactory(data)); + }; + + Spec.prototype.expect = function(actual) { + return this.expectationFactory(actual, this); + }; + + Spec.prototype.execute = function(onComplete) { + var self = this, + timeout; + + this.onStart(this); + + if (this.markedPending || this.disabled) { + complete(); + return; + } + + function timeoutable(fn) { + return function(done) { + timeout = Function.prototype.apply.apply(self.timer.setTimeout, [j$.getGlobal(), [function() { + onException(new Error('Timeout - Async callback was not invoked within timeout specified by jasmine.DEFAULT_TIMEOUT_INTERVAL.')); + done(); + }, j$.DEFAULT_TIMEOUT_INTERVAL]]); + + var callDone = function() { + clearTimeoutable(); + done(); + }; + + fn.call(this, callDone); //TODO: do we care about more than 1 arg? + }; + } + + function clearTimeoutable() { + Function.prototype.apply.apply(self.timer.clearTimeout, [j$.getGlobal(), [timeout]]); + timeout = void 0; + } + + var allFns = this.beforeFns().concat(this.fn).concat(this.afterFns()), + allTimeoutableFns = []; + for (var i = 0; i < allFns.length; i++) { + var fn = allFns[i]; + allTimeoutableFns.push(fn.length > 0 ? timeoutable(fn) : fn); + } + + this.queueRunnerFactory({ + fns: allTimeoutableFns, + onException: onException, + onComplete: complete + }); + + function onException(e) { + clearTimeoutable(); + if (Spec.isPendingSpecException(e)) { + self.pend(); + return; + } + + self.addExpectationResult(false, { + matcherName: "", + passed: false, + expected: "", + actual: "", + error: e + }); + } + + function complete() { + self.result.status = self.status(); + self.resultCallback(self.result); + + if (onComplete) { + onComplete(); + } + } + }; + + Spec.prototype.disable = function() { + this.disabled = true; + }; + + Spec.prototype.pend = function() { + this.markedPending = true; + }; + + Spec.prototype.status = function() { + if (this.disabled) { + return 'disabled'; + } + + if (this.markedPending) { + return 'pending'; + } + + if (this.result.failedExpectations.length > 0) { + return 'failed'; + } else { + return 'passed'; + } + }; + + Spec.prototype.getFullName = function() { + return this.getSpecName(this); + }; + + Spec.pendingSpecExceptionMessage = "=> marked Pending"; + + Spec.isPendingSpecException = function(e) { + return e.toString().indexOf(Spec.pendingSpecExceptionMessage) !== -1; + }; + + return Spec; +}; + +if (typeof window == void 0 && typeof exports == "object") { + exports.Spec = jasmineRequire.Spec; +} + +getJasmineRequireObj().Env = function(j$) { + function Env(options) { + options = options || {}; + + var self = this; + var global = options.global || j$.getGlobal(); + + var totalSpecsDefined = 0; + + var catchExceptions = true; + + var realSetTimeout = j$.getGlobal().setTimeout; + var realClearTimeout = j$.getGlobal().clearTimeout; + this.clock = new j$.Clock(global, new j$.DelayedFunctionScheduler()); + + var runnableLookupTable = {}; + + var spies = []; + + var currentSpec = null; + var currentSuite = null; + + var reporter = new j$.ReportDispatcher([ + "jasmineStarted", + "jasmineDone", + "suiteStarted", + "suiteDone", + "specStarted", + "specDone" + ]); + + this.specFilter = function() { + return true; + }; + + var equalityTesters = []; + + var customEqualityTesters = []; + this.addCustomEqualityTester = function(tester) { + customEqualityTesters.push(tester); + }; + + j$.Expectation.addCoreMatchers(j$.matchers); + + var nextSpecId = 0; + var getNextSpecId = function() { + return 'spec' + nextSpecId++; + }; + + var nextSuiteId = 0; + var getNextSuiteId = function() { + return 'suite' + nextSuiteId++; + }; + + var expectationFactory = function(actual, spec) { + return j$.Expectation.Factory({ + util: j$.matchersUtil, + customEqualityTesters: customEqualityTesters, + actual: actual, + addExpectationResult: addExpectationResult + }); + + function addExpectationResult(passed, result) { + return spec.addExpectationResult(passed, result); + } + }; + + var specStarted = function(spec) { + currentSpec = spec; + reporter.specStarted(spec.result); + }; + + var beforeFns = function(suite) { + return function() { + var befores = []; + while(suite) { + befores = befores.concat(suite.beforeFns); + suite = suite.parentSuite; + } + return befores.reverse(); + }; + }; + + var afterFns = function(suite) { + return function() { + var afters = []; + while(suite) { + afters = afters.concat(suite.afterFns); + suite = suite.parentSuite; + } + return afters; + }; + }; + + var getSpecName = function(spec, suite) { + return suite.getFullName() + ' ' + spec.description; + }; + + // TODO: we may just be able to pass in the fn instead of wrapping here + var buildExpectationResult = j$.buildExpectationResult, + exceptionFormatter = new j$.ExceptionFormatter(), + expectationResultFactory = function(attrs) { + attrs.messageFormatter = exceptionFormatter.message; + attrs.stackFormatter = exceptionFormatter.stack; + + return buildExpectationResult(attrs); + }; + + // TODO: fix this naming, and here's where the value comes in + this.catchExceptions = function(value) { + catchExceptions = !!value; + return catchExceptions; + }; + + this.catchingExceptions = function() { + return catchExceptions; + }; + + var maximumSpecCallbackDepth = 20; + var currentSpecCallbackDepth = 0; + + function clearStack(fn) { + currentSpecCallbackDepth++; + if (currentSpecCallbackDepth >= maximumSpecCallbackDepth) { + currentSpecCallbackDepth = 0; + realSetTimeout(fn, 0); + } else { + fn(); + } + } + + var catchException = function(e) { + return j$.Spec.isPendingSpecException(e) || catchExceptions; + }; + + var queueRunnerFactory = function(options) { + options.catchException = catchException; + options.clearStack = options.clearStack || clearStack; + + new j$.QueueRunner(options).execute(); + }; + + var topSuite = new j$.Suite({ + env: this, + id: getNextSuiteId(), + description: 'Jasmine__TopLevel__Suite', + queueRunner: queueRunnerFactory, + resultCallback: function() {} // TODO - hook this up + }); + runnableLookupTable[topSuite.id] = topSuite; + currentSuite = topSuite; + + this.topSuite = function() { + return topSuite; + }; + + this.execute = function(runnablesToRun) { + runnablesToRun = runnablesToRun || [topSuite.id]; + + var allFns = []; + for(var i = 0; i < runnablesToRun.length; i++) { + var runnable = runnableLookupTable[runnablesToRun[i]]; + allFns.push((function(runnable) { return function(done) { runnable.execute(done); }; })(runnable)); + } + + reporter.jasmineStarted({ + totalSpecsDefined: totalSpecsDefined + }); + + queueRunnerFactory({fns: allFns, onComplete: reporter.jasmineDone}); + }; + + this.addReporter = function(reporterToAdd) { + reporter.addReporter(reporterToAdd); + }; + + this.addMatchers = function(matchersToAdd) { + j$.Expectation.addMatchers(matchersToAdd); + }; + + this.spyOn = function(obj, methodName) { + if (j$.util.isUndefined(obj)) { + throw new Error("spyOn could not find an object to spy upon for " + methodName + "()"); + } + + if (j$.util.isUndefined(obj[methodName])) { + throw new Error(methodName + '() method does not exist'); + } + + if (obj[methodName] && j$.isSpy(obj[methodName])) { + //TODO?: should this return the current spy? Downside: may cause user confusion about spy state + throw new Error(methodName + ' has already been spied upon'); + } + + var spy = j$.createSpy(methodName, obj[methodName]); + + spies.push({ + spy: spy, + baseObj: obj, + methodName: methodName, + originalValue: obj[methodName] + }); + + obj[methodName] = spy; + + return spy; + }; + + var suiteFactory = function(description) { + var suite = new j$.Suite({ + env: self, + id: getNextSuiteId(), + description: description, + parentSuite: currentSuite, + queueRunner: queueRunnerFactory, + onStart: suiteStarted, + resultCallback: function(attrs) { + reporter.suiteDone(attrs); + } + }); + + runnableLookupTable[suite.id] = suite; + return suite; + }; + + this.describe = function(description, specDefinitions) { + var suite = suiteFactory(description); + + var parentSuite = currentSuite; + parentSuite.addChild(suite); + currentSuite = suite; + + var declarationError = null; + try { + specDefinitions.call(suite); + } catch (e) { + declarationError = e; + } + + if (declarationError) { + this.it("encountered a declaration exception", function() { + throw declarationError; + }); + } + + currentSuite = parentSuite; + + return suite; + }; + + this.xdescribe = function(description, specDefinitions) { + var suite = this.describe(description, specDefinitions); + suite.disable(); + return suite; + }; + + var specFactory = function(description, fn, suite) { + totalSpecsDefined++; + + var spec = new j$.Spec({ + id: getNextSpecId(), + beforeFns: beforeFns(suite), + afterFns: afterFns(suite), + expectationFactory: expectationFactory, + exceptionFormatter: exceptionFormatter, + resultCallback: specResultCallback, + getSpecName: function(spec) { + return getSpecName(spec, suite); + }, + onStart: specStarted, + description: description, + expectationResultFactory: expectationResultFactory, + queueRunnerFactory: queueRunnerFactory, + fn: fn, + timer: {setTimeout: realSetTimeout, clearTimeout: realClearTimeout} + }); + + runnableLookupTable[spec.id] = spec; + + if (!self.specFilter(spec)) { + spec.disable(); + } + + return spec; + + function removeAllSpies() { + for (var i = 0; i < spies.length; i++) { + var spyEntry = spies[i]; + spyEntry.baseObj[spyEntry.methodName] = spyEntry.originalValue; + } + spies = []; + } + + function specResultCallback(result) { + removeAllSpies(); + j$.Expectation.resetMatchers(); + customEqualityTesters = []; + currentSpec = null; + reporter.specDone(result); + } + }; + + var suiteStarted = function(suite) { + reporter.suiteStarted(suite.result); + }; + + this.it = function(description, fn) { + var spec = specFactory(description, fn, currentSuite); + currentSuite.addChild(spec); + return spec; + }; + + this.xit = function(description, fn) { + var spec = this.it(description, fn); + spec.pend(); + return spec; + }; + + this.expect = function(actual) { + return currentSpec.expect(actual); + }; + + this.beforeEach = function(beforeEachFunction) { + currentSuite.beforeEach(beforeEachFunction); + }; + + this.afterEach = function(afterEachFunction) { + currentSuite.afterEach(afterEachFunction); + }; + + this.pending = function() { + throw j$.Spec.pendingSpecExceptionMessage; + }; + } + + return Env; +}; + +getJasmineRequireObj().JsApiReporter = function() { + + var noopTimer = { + start: function(){}, + elapsed: function(){ return 0; } + }; + + function JsApiReporter(options) { + var timer = options.timer || noopTimer, + status = "loaded"; + + this.started = false; + this.finished = false; + + this.jasmineStarted = function() { + this.started = true; + status = 'started'; + timer.start(); + }; + + var executionTime; + + this.jasmineDone = function() { + this.finished = true; + executionTime = timer.elapsed(); + status = 'done'; + }; + + this.status = function() { + return status; + }; + + var suites = {}; + + this.suiteStarted = function(result) { + storeSuite(result); + }; + + this.suiteDone = function(result) { + storeSuite(result); + }; + + function storeSuite(result) { + suites[result.id] = result; + } + + this.suites = function() { + return suites; + }; + + var specs = []; + this.specStarted = function(result) { }; + + this.specDone = function(result) { + specs.push(result); + }; + + this.specResults = function(index, length) { + return specs.slice(index, index + length); + }; + + this.specs = function() { + return specs; + }; + + this.executionTime = function() { + return executionTime; + }; + + } + + return JsApiReporter; +}; + +getJasmineRequireObj().Any = function() { + + function Any(expectedObject) { + this.expectedObject = expectedObject; + } + + Any.prototype.jasmineMatches = function(other) { + if (this.expectedObject == String) { + return typeof other == 'string' || other instanceof String; + } + + if (this.expectedObject == Number) { + return typeof other == 'number' || other instanceof Number; + } + + if (this.expectedObject == Function) { + return typeof other == 'function' || other instanceof Function; + } + + if (this.expectedObject == Object) { + return typeof other == 'object'; + } + + if (this.expectedObject == Boolean) { + return typeof other == 'boolean'; + } + + return other instanceof this.expectedObject; + }; + + Any.prototype.jasmineToString = function() { + return ''; + }; + + return Any; +}; + +getJasmineRequireObj().CallTracker = function() { + + function CallTracker() { + var calls = []; + + this.track = function(context) { + calls.push(context); + }; + + this.any = function() { + return !!calls.length; + }; + + this.count = function() { + return calls.length; + }; + + this.argsFor = function(index) { + var call = calls[index]; + return call ? call.args : []; + }; + + this.all = function() { + return calls; + }; + + this.allArgs = function() { + var callArgs = []; + for(var i = 0; i < calls.length; i++){ + callArgs.push(calls[i].args); + } + + return callArgs; + }; + + this.first = function() { + return calls[0]; + }; + + this.mostRecent = function() { + return calls[calls.length - 1]; + }; + + this.reset = function() { + calls = []; + }; + } + + return CallTracker; +}; + +getJasmineRequireObj().Clock = function() { + function Clock(global, delayedFunctionScheduler) { + var self = this, + realTimingFunctions = { + setTimeout: global.setTimeout, + clearTimeout: global.clearTimeout, + setInterval: global.setInterval, + clearInterval: global.clearInterval + }, + fakeTimingFunctions = { + setTimeout: setTimeout, + clearTimeout: clearTimeout, + setInterval: setInterval, + clearInterval: clearInterval + }, + installed = false, + timer; + + self.install = function() { + replace(global, fakeTimingFunctions); + timer = fakeTimingFunctions; + installed = true; + }; + + self.uninstall = function() { + delayedFunctionScheduler.reset(); + replace(global, realTimingFunctions); + timer = realTimingFunctions; + installed = false; + }; + + self.setTimeout = function(fn, delay, params) { + if (legacyIE()) { + if (arguments.length > 2) { + throw new Error("IE < 9 cannot support extra params to setTimeout without a polyfill"); + } + return timer.setTimeout(fn, delay); + } + return Function.prototype.apply.apply(timer.setTimeout, [global, arguments]); + }; + + self.setInterval = function(fn, delay, params) { + if (legacyIE()) { + if (arguments.length > 2) { + throw new Error("IE < 9 cannot support extra params to setInterval without a polyfill"); + } + return timer.setInterval(fn, delay); + } + return Function.prototype.apply.apply(timer.setInterval, [global, arguments]); + }; + + self.clearTimeout = function(id) { + return Function.prototype.call.apply(timer.clearTimeout, [global, id]); + }; + + self.clearInterval = function(id) { + return Function.prototype.call.apply(timer.clearInterval, [global, id]); + }; + + self.tick = function(millis) { + if (installed) { + delayedFunctionScheduler.tick(millis); + } else { + throw new Error("Mock clock is not installed, use jasmine.clock().install()"); + } + }; + + return self; + + function legacyIE() { + //if these methods are polyfilled, apply will be present + return !(realTimingFunctions.setTimeout || realTimingFunctions.setInterval).apply; + } + + function replace(dest, source) { + for (var prop in source) { + dest[prop] = source[prop]; + } + } + + function setTimeout(fn, delay) { + return delayedFunctionScheduler.scheduleFunction(fn, delay, argSlice(arguments, 2)); + } + + function clearTimeout(id) { + return delayedFunctionScheduler.removeFunctionWithId(id); + } + + function setInterval(fn, interval) { + return delayedFunctionScheduler.scheduleFunction(fn, interval, argSlice(arguments, 2), true); + } + + function clearInterval(id) { + return delayedFunctionScheduler.removeFunctionWithId(id); + } + + function argSlice(argsObj, n) { + return Array.prototype.slice.call(argsObj, 2); + } + } + + return Clock; +}; + +getJasmineRequireObj().DelayedFunctionScheduler = function() { + function DelayedFunctionScheduler() { + var self = this; + var scheduledLookup = []; + var scheduledFunctions = {}; + var currentTime = 0; + var delayedFnCount = 0; + + self.tick = function(millis) { + millis = millis || 0; + var endTime = currentTime + millis; + + runScheduledFunctions(endTime); + currentTime = endTime; + }; + + self.scheduleFunction = function(funcToCall, millis, params, recurring, timeoutKey, runAtMillis) { + var f; + if (typeof(funcToCall) === 'string') { + /* jshint evil: true */ + f = function() { return eval(funcToCall); }; + /* jshint evil: false */ + } else { + f = funcToCall; + } + + millis = millis || 0; + timeoutKey = timeoutKey || ++delayedFnCount; + runAtMillis = runAtMillis || (currentTime + millis); + + var funcToSchedule = { + runAtMillis: runAtMillis, + funcToCall: f, + recurring: recurring, + params: params, + timeoutKey: timeoutKey, + millis: millis + }; + + if (runAtMillis in scheduledFunctions) { + scheduledFunctions[runAtMillis].push(funcToSchedule); + } else { + scheduledFunctions[runAtMillis] = [funcToSchedule]; + scheduledLookup.push(runAtMillis); + scheduledLookup.sort(function (a, b) { + return a - b; + }); + } + + return timeoutKey; + }; + + self.removeFunctionWithId = function(timeoutKey) { + for (var runAtMillis in scheduledFunctions) { + var funcs = scheduledFunctions[runAtMillis]; + var i = indexOfFirstToPass(funcs, function (func) { + return func.timeoutKey === timeoutKey; + }); + + if (i > -1) { + if (funcs.length === 1) { + delete scheduledFunctions[runAtMillis]; + deleteFromLookup(runAtMillis); + } else { + funcs.splice(i, 1); + } + + // intervals get rescheduled when executed, so there's never more + // than a single scheduled function with a given timeoutKey + break; + } + } + }; + + self.reset = function() { + currentTime = 0; + scheduledLookup = []; + scheduledFunctions = {}; + delayedFnCount = 0; + }; + + return self; + + function indexOfFirstToPass(array, testFn) { + var index = -1; + + for (var i = 0; i < array.length; ++i) { + if (testFn(array[i])) { + index = i; + break; + } + } + + return index; + } + + function deleteFromLookup(key) { + var value = Number(key); + var i = indexOfFirstToPass(scheduledLookup, function (millis) { + return millis === value; + }); + + if (i > -1) { + scheduledLookup.splice(i, 1); + } + } + + function reschedule(scheduledFn) { + self.scheduleFunction(scheduledFn.funcToCall, + scheduledFn.millis, + scheduledFn.params, + true, + scheduledFn.timeoutKey, + scheduledFn.runAtMillis + scheduledFn.millis); + } + + function runScheduledFunctions(endTime) { + if (scheduledLookup.length === 0 || scheduledLookup[0] > endTime) { + return; + } + + do { + currentTime = scheduledLookup.shift(); + + var funcsToRun = scheduledFunctions[currentTime]; + delete scheduledFunctions[currentTime]; + + for (var i = 0; i < funcsToRun.length; ++i) { + var funcToRun = funcsToRun[i]; + funcToRun.funcToCall.apply(null, funcToRun.params || []); + + if (funcToRun.recurring) { + reschedule(funcToRun); + } + } + } while (scheduledLookup.length > 0 && + // checking first if we're out of time prevents setTimeout(0) + // scheduled in a funcToRun from forcing an extra iteration + currentTime !== endTime && + scheduledLookup[0] <= endTime); + } + } + + return DelayedFunctionScheduler; +}; + +getJasmineRequireObj().ExceptionFormatter = function() { + function ExceptionFormatter() { + this.message = function(error) { + var message = error.name + + ': ' + + error.message; + + if (error.fileName || error.sourceURL) { + message += " in " + (error.fileName || error.sourceURL); + } + + if (error.line || error.lineNumber) { + message += " (line " + (error.line || error.lineNumber) + ")"; + } + + return message; + }; + + this.stack = function(error) { + return error ? error.stack : null; + }; + } + + return ExceptionFormatter; +}; + +getJasmineRequireObj().Expectation = function() { + + var matchers = {}; + + function Expectation(options) { + this.util = options.util || { buildFailureMessage: function() {} }; + this.customEqualityTesters = options.customEqualityTesters || []; + this.actual = options.actual; + this.addExpectationResult = options.addExpectationResult || function(){}; + this.isNot = options.isNot; + + for (var matcherName in matchers) { + this[matcherName] = matchers[matcherName]; + } + } + + Expectation.prototype.wrapCompare = function(name, matcherFactory) { + return function() { + var args = Array.prototype.slice.call(arguments, 0), + expected = args.slice(0), + message = ""; + + args.unshift(this.actual); + + var matcher = matcherFactory(this.util, this.customEqualityTesters), + matcherCompare = matcher.compare; + + function defaultNegativeCompare() { + var result = matcher.compare.apply(null, args); + result.pass = !result.pass; + return result; + } + + if (this.isNot) { + matcherCompare = matcher.negativeCompare || defaultNegativeCompare; + } + + var result = matcherCompare.apply(null, args); + + if (!result.pass) { + if (!result.message) { + args.unshift(this.isNot); + args.unshift(name); + message = this.util.buildFailureMessage.apply(null, args); + } else { + message = result.message; + } + } + + if (expected.length == 1) { + expected = expected[0]; + } + + // TODO: how many of these params are needed? + this.addExpectationResult( + result.pass, + { + matcherName: name, + passed: result.pass, + message: message, + actual: this.actual, + expected: expected // TODO: this may need to be arrayified/sliced + } + ); + }; + }; + + Expectation.addCoreMatchers = function(matchers) { + var prototype = Expectation.prototype; + for (var matcherName in matchers) { + var matcher = matchers[matcherName]; + prototype[matcherName] = prototype.wrapCompare(matcherName, matcher); + } + }; + + Expectation.addMatchers = function(matchersToAdd) { + for (var name in matchersToAdd) { + var matcher = matchersToAdd[name]; + matchers[name] = Expectation.prototype.wrapCompare(name, matcher); + } + }; + + Expectation.resetMatchers = function() { + for (var name in matchers) { + delete matchers[name]; + } + }; + + Expectation.Factory = function(options) { + options = options || {}; + + var expect = new Expectation(options); + + // TODO: this would be nice as its own Object - NegativeExpectation + // TODO: copy instead of mutate options + options.isNot = true; + expect.not = new Expectation(options); + + return expect; + }; + + return Expectation; +}; + +//TODO: expectation result may make more sense as a presentation of an expectation. +getJasmineRequireObj().buildExpectationResult = function() { + function buildExpectationResult(options) { + var messageFormatter = options.messageFormatter || function() {}, + stackFormatter = options.stackFormatter || function() {}; + + return { + matcherName: options.matcherName, + expected: options.expected, + actual: options.actual, + message: message(), + stack: stack(), + passed: options.passed + }; + + function message() { + if (options.passed) { + return "Passed."; + } else if (options.message) { + return options.message; + } else if (options.error) { + return messageFormatter(options.error); + } + return ""; + } + + function stack() { + if (options.passed) { + return ""; + } + + var error = options.error; + if (!error) { + try { + throw new Error(message()); + } catch (e) { + error = e; + } + } + return stackFormatter(error); + } + } + + return buildExpectationResult; +}; + +getJasmineRequireObj().ObjectContaining = function(j$) { + + function ObjectContaining(sample) { + this.sample = sample; + } + + ObjectContaining.prototype.jasmineMatches = function(other, mismatchKeys, mismatchValues) { + if (typeof(this.sample) !== "object") { throw new Error("You must provide an object to objectContaining, not '"+this.sample+"'."); } + + mismatchKeys = mismatchKeys || []; + mismatchValues = mismatchValues || []; + + var hasKey = function(obj, keyName) { + return obj !== null && !j$.util.isUndefined(obj[keyName]); + }; + + for (var property in this.sample) { + if (!hasKey(other, property) && hasKey(this.sample, property)) { + mismatchKeys.push("expected has key '" + property + "', but missing from actual."); + } + else if (!j$.matchersUtil.equals(this.sample[property], other[property])) { + mismatchValues.push("'" + property + "' was '" + (other[property] ? j$.util.htmlEscape(other[property].toString()) : other[property]) + "' in actual, but was '" + (this.sample[property] ? j$.util.htmlEscape(this.sample[property].toString()) : this.sample[property]) + "' in expected."); + } + } + + return (mismatchKeys.length === 0 && mismatchValues.length === 0); + }; + + ObjectContaining.prototype.jasmineToString = function() { + return ""; + }; + + return ObjectContaining; +}; + +getJasmineRequireObj().pp = function(j$) { + + function PrettyPrinter() { + this.ppNestLevel_ = 0; + } + + PrettyPrinter.prototype.format = function(value) { + this.ppNestLevel_++; + try { + if (j$.util.isUndefined(value)) { + this.emitScalar('undefined'); + } else if (value === null) { + this.emitScalar('null'); + } else if (value === j$.getGlobal()) { + this.emitScalar(''); + } else if (value.jasmineToString) { + this.emitScalar(value.jasmineToString()); + } else if (typeof value === 'string') { + this.emitString(value); + } else if (j$.isSpy(value)) { + this.emitScalar("spy on " + value.and.identity()); + } else if (value instanceof RegExp) { + this.emitScalar(value.toString()); + } else if (typeof value === 'function') { + this.emitScalar('Function'); + } else if (typeof value.nodeType === 'number') { + this.emitScalar('HTMLNode'); + } else if (value instanceof Date) { + this.emitScalar('Date(' + value + ')'); + } else if (value.__Jasmine_been_here_before__) { + this.emitScalar(''); + } else if (j$.isArray_(value) || j$.isA_('Object', value)) { + value.__Jasmine_been_here_before__ = true; + if (j$.isArray_(value)) { + this.emitArray(value); + } else { + this.emitObject(value); + } + delete value.__Jasmine_been_here_before__; + } else { + this.emitScalar(value.toString()); + } + } finally { + this.ppNestLevel_--; + } + }; + + PrettyPrinter.prototype.iterateObject = function(obj, fn) { + for (var property in obj) { + if (!obj.hasOwnProperty(property)) { continue; } + if (property == '__Jasmine_been_here_before__') { continue; } + fn(property, obj.__lookupGetter__ ? (!j$.util.isUndefined(obj.__lookupGetter__(property)) && + obj.__lookupGetter__(property) !== null) : false); + } + }; + + PrettyPrinter.prototype.emitArray = j$.unimplementedMethod_; + PrettyPrinter.prototype.emitObject = j$.unimplementedMethod_; + PrettyPrinter.prototype.emitScalar = j$.unimplementedMethod_; + PrettyPrinter.prototype.emitString = j$.unimplementedMethod_; + + function StringPrettyPrinter() { + PrettyPrinter.call(this); + + this.string = ''; + } + + j$.util.inherit(StringPrettyPrinter, PrettyPrinter); + + StringPrettyPrinter.prototype.emitScalar = function(value) { + this.append(value); + }; + + StringPrettyPrinter.prototype.emitString = function(value) { + this.append("'" + value + "'"); + }; + + StringPrettyPrinter.prototype.emitArray = function(array) { + if (this.ppNestLevel_ > j$.MAX_PRETTY_PRINT_DEPTH) { + this.append("Array"); + return; + } + + this.append('[ '); + for (var i = 0; i < array.length; i++) { + if (i > 0) { + this.append(', '); + } + this.format(array[i]); + } + this.append(' ]'); + }; + + StringPrettyPrinter.prototype.emitObject = function(obj) { + if (this.ppNestLevel_ > j$.MAX_PRETTY_PRINT_DEPTH) { + this.append("Object"); + return; + } + + var self = this; + this.append('{ '); + var first = true; + + this.iterateObject(obj, function(property, isGetter) { + if (first) { + first = false; + } else { + self.append(', '); + } + + self.append(property); + self.append(' : '); + if (isGetter) { + self.append(''); + } else { + self.format(obj[property]); + } + }); + + this.append(' }'); + }; + + StringPrettyPrinter.prototype.append = function(value) { + this.string += value; + }; + + return function(value) { + var stringPrettyPrinter = new StringPrettyPrinter(); + stringPrettyPrinter.format(value); + return stringPrettyPrinter.string; + }; +}; + +getJasmineRequireObj().QueueRunner = function() { + + function QueueRunner(attrs) { + this.fns = attrs.fns || []; + this.onComplete = attrs.onComplete || function() {}; + this.clearStack = attrs.clearStack || function(fn) {fn();}; + this.onException = attrs.onException || function() {}; + this.catchException = attrs.catchException || function() { return true; }; + this.userContext = {}; + } + + QueueRunner.prototype.execute = function() { + this.run(this.fns, 0); + }; + + QueueRunner.prototype.run = function(fns, recursiveIndex) { + var length = fns.length, + self = this, + iterativeIndex; + + for(iterativeIndex = recursiveIndex; iterativeIndex < length; iterativeIndex++) { + var fn = fns[iterativeIndex]; + if (fn.length > 0) { + return attemptAsync(fn); + } else { + attemptSync(fn); + } + } + + var runnerDone = iterativeIndex >= length; + + if (runnerDone) { + this.clearStack(this.onComplete); + } + + function attemptSync(fn) { + try { + fn.call(self.userContext); + } catch (e) { + handleException(e); + } + } + + function attemptAsync(fn) { + var next = function () { self.run(fns, iterativeIndex + 1); }; + + try { + fn.call(self.userContext, next); + } catch (e) { + handleException(e); + next(); + } + } + + function handleException(e) { + self.onException(e); + if (!self.catchException(e)) { + //TODO: set a var when we catch an exception and + //use a finally block to close the loop in a nice way.. + throw e; + } + } + }; + + return QueueRunner; +}; + +getJasmineRequireObj().ReportDispatcher = function() { + function ReportDispatcher(methods) { + + var dispatchedMethods = methods || []; + + for (var i = 0; i < dispatchedMethods.length; i++) { + var method = dispatchedMethods[i]; + this[method] = (function(m) { + return function() { + dispatch(m, arguments); + }; + }(method)); + } + + var reporters = []; + + this.addReporter = function(reporter) { + reporters.push(reporter); + }; + + return this; + + function dispatch(method, args) { + for (var i = 0; i < reporters.length; i++) { + var reporter = reporters[i]; + if (reporter[method]) { + reporter[method].apply(reporter, args); + } + } + } + } + + return ReportDispatcher; +}; + + +getJasmineRequireObj().SpyStrategy = function() { + + function SpyStrategy(options) { + options = options || {}; + + var identity = options.name || "unknown", + originalFn = options.fn || function() {}, + getSpy = options.getSpy || function() {}, + plan = function() {}; + + this.identity = function() { + return identity; + }; + + this.exec = function() { + return plan.apply(this, arguments); + }; + + this.callThrough = function() { + plan = originalFn; + return getSpy(); + }; + + this.returnValue = function(value) { + plan = function() { + return value; + }; + return getSpy(); + }; + + this.throwError = function(something) { + var error = (something instanceof Error) ? something : new Error(something); + plan = function() { + throw error; + }; + return getSpy(); + }; + + this.callFake = function(fn) { + plan = fn; + return getSpy(); + }; + + this.stub = function(fn) { + plan = function() {}; + return getSpy(); + }; + } + + return SpyStrategy; +}; + +getJasmineRequireObj().Suite = function() { + function Suite(attrs) { + this.env = attrs.env; + this.id = attrs.id; + this.parentSuite = attrs.parentSuite; + this.description = attrs.description; + this.onStart = attrs.onStart || function() {}; + this.resultCallback = attrs.resultCallback || function() {}; + this.clearStack = attrs.clearStack || function(fn) {fn();}; + + this.beforeFns = []; + this.afterFns = []; + this.queueRunner = attrs.queueRunner || function() {}; + this.disabled = false; + + this.children = []; + + this.result = { + id: this.id, + status: this.disabled ? 'disabled' : '', + description: this.description, + fullName: this.getFullName() + }; + } + + Suite.prototype.getFullName = function() { + var fullName = this.description; + for (var parentSuite = this.parentSuite; parentSuite; parentSuite = parentSuite.parentSuite) { + if (parentSuite.parentSuite) { + fullName = parentSuite.description + ' ' + fullName; + } + } + return fullName; + }; + + Suite.prototype.disable = function() { + this.disabled = true; + }; + + Suite.prototype.beforeEach = function(fn) { + this.beforeFns.unshift(fn); + }; + + Suite.prototype.afterEach = function(fn) { + this.afterFns.unshift(fn); + }; + + Suite.prototype.addChild = function(child) { + this.children.push(child); + }; + + Suite.prototype.execute = function(onComplete) { + var self = this; + if (this.disabled) { + complete(); + return; + } + + var allFns = []; + + for (var i = 0; i < this.children.length; i++) { + allFns.push(wrapChildAsAsync(this.children[i])); + } + + this.onStart(this); + + this.queueRunner({ + fns: allFns, + onComplete: complete + }); + + function complete() { + self.resultCallback(self.result); + + if (onComplete) { + onComplete(); + } + } + + function wrapChildAsAsync(child) { + return function(done) { child.execute(done); }; + } + }; + + return Suite; +}; + +if (typeof window == void 0 && typeof exports == "object") { + exports.Suite = jasmineRequire.Suite; +} + +getJasmineRequireObj().Timer = function() { + function Timer(options) { + options = options || {}; + + var now = options.now || function() { return new Date().getTime(); }, + startTime; + + this.start = function() { + startTime = now(); + }; + + this.elapsed = function() { + return now() - startTime; + }; + } + + return Timer; +}; + +getJasmineRequireObj().matchersUtil = function(j$) { + // TODO: what to do about jasmine.pp not being inject? move to JSON.stringify? gut PrettyPrinter? + + return { + equals: function(a, b, customTesters) { + customTesters = customTesters || []; + + return eq(a, b, [], [], customTesters); + }, + + contains: function(haystack, needle, customTesters) { + customTesters = customTesters || []; + + if (Object.prototype.toString.apply(haystack) === "[object Array]") { + for (var i = 0; i < haystack.length; i++) { + if (eq(haystack[i], needle, [], [], customTesters)) { + return true; + } + } + return false; + } + return haystack.indexOf(needle) >= 0; + }, + + buildFailureMessage: function() { + var args = Array.prototype.slice.call(arguments, 0), + matcherName = args[0], + isNot = args[1], + actual = args[2], + expected = args.slice(3), + englishyPredicate = matcherName.replace(/[A-Z]/g, function(s) { return ' ' + s.toLowerCase(); }); + + var message = "Expected " + + j$.pp(actual) + + (isNot ? " not " : " ") + + englishyPredicate; + + if (expected.length > 0) { + for (var i = 0; i < expected.length; i++) { + if (i > 0) { + message += ","; + } + message += " " + j$.pp(expected[i]); + } + } + + return message + "."; + } + }; + + // Equality function lovingly adapted from isEqual in + // [Underscore](http://underscorejs.org) + function eq(a, b, aStack, bStack, customTesters) { + var result = true; + + for (var i = 0; i < customTesters.length; i++) { + var customTesterResult = customTesters[i](a, b); + if (!j$.util.isUndefined(customTesterResult)) { + return customTesterResult; + } + } + + if (a instanceof j$.Any) { + result = a.jasmineMatches(b); + if (result) { + return true; + } + } + + if (b instanceof j$.Any) { + result = b.jasmineMatches(a); + if (result) { + return true; + } + } + + if (b instanceof j$.ObjectContaining) { + result = b.jasmineMatches(a); + if (result) { + return true; + } + } + + if (a instanceof Error && b instanceof Error) { + return a.message == b.message; + } + + // Identical objects are equal. `0 === -0`, but they aren't identical. + // See the [Harmony `egal` proposal](http://wiki.ecmascript.org/doku.php?id=harmony:egal). + if (a === b) { return a !== 0 || 1 / a == 1 / b; } + // A strict comparison is necessary because `null == undefined`. + if (a === null || b === null) { return a === b; } + var className = Object.prototype.toString.call(a); + if (className != Object.prototype.toString.call(b)) { return false; } + switch (className) { + // Strings, numbers, dates, and booleans are compared by value. + case '[object String]': + // Primitives and their corresponding object wrappers are equivalent; thus, `"5"` is + // equivalent to `new String("5")`. + return a == String(b); + case '[object Number]': + // `NaN`s are equivalent, but non-reflexive. An `egal` comparison is performed for + // other numeric values. + return a != +a ? b != +b : (a === 0 ? 1 / a == 1 / b : a == +b); + case '[object Date]': + case '[object Boolean]': + // Coerce dates and booleans to numeric primitive values. Dates are compared by their + // millisecond representations. Note that invalid dates with millisecond representations + // of `NaN` are not equivalent. + return +a == +b; + // RegExps are compared by their source patterns and flags. + case '[object RegExp]': + return a.source == b.source && + a.global == b.global && + a.multiline == b.multiline && + a.ignoreCase == b.ignoreCase; + } + if (typeof a != 'object' || typeof b != 'object') { return false; } + // Assume equality for cyclic structures. The algorithm for detecting cyclic + // structures is adapted from ES 5.1 section 15.12.3, abstract operation `JO`. + var length = aStack.length; + while (length--) { + // Linear search. Performance is inversely proportional to the number of + // unique nested structures. + if (aStack[length] == a) { return bStack[length] == b; } + } + // Add the first object to the stack of traversed objects. + aStack.push(a); + bStack.push(b); + var size = 0; + // Recursively compare objects and arrays. + if (className == '[object Array]') { + // Compare array lengths to determine if a deep comparison is necessary. + size = a.length; + result = size == b.length; + if (result) { + // Deep compare the contents, ignoring non-numeric properties. + while (size--) { + if (!(result = eq(a[size], b[size], aStack, bStack, customTesters))) { break; } + } + } + } else { + // Objects with different constructors are not equivalent, but `Object`s + // from different frames are. + var aCtor = a.constructor, bCtor = b.constructor; + if (aCtor !== bCtor && !(isFunction(aCtor) && (aCtor instanceof aCtor) && + isFunction(bCtor) && (bCtor instanceof bCtor))) { + return false; + } + // Deep compare objects. + for (var key in a) { + if (has(a, key)) { + // Count the expected number of properties. + size++; + // Deep compare each member. + if (!(result = has(b, key) && eq(a[key], b[key], aStack, bStack, customTesters))) { break; } + } + } + // Ensure that both objects contain the same number of properties. + if (result) { + for (key in b) { + if (has(b, key) && !(size--)) { break; } + } + result = !size; + } + } + // Remove the first object from the stack of traversed objects. + aStack.pop(); + bStack.pop(); + + return result; + + function has(obj, key) { + return obj.hasOwnProperty(key); + } + + function isFunction(obj) { + return typeof obj === 'function'; + } + } +}; + +getJasmineRequireObj().toBe = function() { + function toBe() { + return { + compare: function(actual, expected) { + return { + pass: actual === expected + }; + } + }; + } + + return toBe; +}; + +getJasmineRequireObj().toBeCloseTo = function() { + + function toBeCloseTo() { + return { + compare: function(actual, expected, precision) { + if (precision !== 0) { + precision = precision || 2; + } + + return { + pass: Math.abs(expected - actual) < (Math.pow(10, -precision) / 2) + }; + } + }; + } + + return toBeCloseTo; +}; + +getJasmineRequireObj().toBeDefined = function() { + function toBeDefined() { + return { + compare: function(actual) { + return { + pass: (void 0 !== actual) + }; + } + }; + } + + return toBeDefined; +}; + +getJasmineRequireObj().toBeFalsy = function() { + function toBeFalsy() { + return { + compare: function(actual) { + return { + pass: !!!actual + }; + } + }; + } + + return toBeFalsy; +}; + +getJasmineRequireObj().toBeGreaterThan = function() { + + function toBeGreaterThan() { + return { + compare: function(actual, expected) { + return { + pass: actual > expected + }; + } + }; + } + + return toBeGreaterThan; +}; + + +getJasmineRequireObj().toBeLessThan = function() { + function toBeLessThan() { + return { + + compare: function(actual, expected) { + return { + pass: actual < expected + }; + } + }; + } + + return toBeLessThan; +}; +getJasmineRequireObj().toBeNaN = function(j$) { + + function toBeNaN() { + return { + compare: function(actual) { + var result = { + pass: (actual !== actual) + }; + + if (result.pass) { + result.message = "Expected actual not to be NaN."; + } else { + result.message = "Expected " + j$.pp(actual) + " to be NaN."; + } + + return result; + } + }; + } + + return toBeNaN; +}; + +getJasmineRequireObj().toBeNull = function() { + + function toBeNull() { + return { + compare: function(actual) { + return { + pass: actual === null + }; + } + }; + } + + return toBeNull; +}; + +getJasmineRequireObj().toBeTruthy = function() { + + function toBeTruthy() { + return { + compare: function(actual) { + return { + pass: !!actual + }; + } + }; + } + + return toBeTruthy; +}; + +getJasmineRequireObj().toBeUndefined = function() { + + function toBeUndefined() { + return { + compare: function(actual) { + return { + pass: void 0 === actual + }; + } + }; + } + + return toBeUndefined; +}; + +getJasmineRequireObj().toContain = function() { + function toContain(util, customEqualityTesters) { + customEqualityTesters = customEqualityTesters || []; + + return { + compare: function(actual, expected) { + + return { + pass: util.contains(actual, expected, customEqualityTesters) + }; + } + }; + } + + return toContain; +}; + +getJasmineRequireObj().toEqual = function() { + + function toEqual(util, customEqualityTesters) { + customEqualityTesters = customEqualityTesters || []; + + return { + compare: function(actual, expected) { + var result = { + pass: false + }; + + result.pass = util.equals(actual, expected, customEqualityTesters); + + return result; + } + }; + } + + return toEqual; +}; + +getJasmineRequireObj().toHaveBeenCalled = function(j$) { + + function toHaveBeenCalled() { + return { + compare: function(actual) { + var result = {}; + + if (!j$.isSpy(actual)) { + throw new Error('Expected a spy, but got ' + j$.pp(actual) + '.'); + } + + if (arguments.length > 1) { + throw new Error('toHaveBeenCalled does not take arguments, use toHaveBeenCalledWith'); + } + + result.pass = actual.calls.any(); + + result.message = result.pass ? + "Expected spy " + actual.and.identity() + " not to have been called." : + "Expected spy " + actual.and.identity() + " to have been called."; + + return result; + } + }; + } + + return toHaveBeenCalled; +}; + +getJasmineRequireObj().toHaveBeenCalledWith = function(j$) { + + function toHaveBeenCalledWith(util) { + return { + compare: function() { + var args = Array.prototype.slice.call(arguments, 0), + actual = args[0], + expectedArgs = args.slice(1), + result = { pass: false }; + + if (!j$.isSpy(actual)) { + throw new Error('Expected a spy, but got ' + j$.pp(actual) + '.'); + } + + if (!actual.calls.any()) { + result.message = "Expected spy " + actual.and.identity() + " to have been called with " + j$.pp(expectedArgs) + " but it was never called."; + return result; + } + + if (util.contains(actual.calls.allArgs(), expectedArgs)) { + result.pass = true; + result.message = "Expected spy " + actual.and.identity() + " not to have been called with " + j$.pp(expectedArgs) + " but it was."; + } else { + result.message = "Expected spy " + actual.and.identity() + " to have been called with " + j$.pp(expectedArgs) + " but actual calls were " + j$.pp(actual.calls.allArgs()).replace(/^\[ | \]$/g, '') + "."; + } + + return result; + } + }; + } + + return toHaveBeenCalledWith; +}; + +getJasmineRequireObj().toMatch = function() { + + function toMatch() { + return { + compare: function(actual, expected) { + var regexp = new RegExp(expected); + + return { + pass: regexp.test(actual) + }; + } + }; + } + + return toMatch; +}; + +getJasmineRequireObj().toThrow = function(j$) { + + function toThrow(util) { + return { + compare: function(actual, expected) { + var result = { pass: false }, + threw = false, + thrown; + + if (typeof actual != "function") { + throw new Error("Actual is not a Function"); + } + + try { + actual(); + } catch (e) { + threw = true; + thrown = e; + } + + if (!threw) { + result.message = "Expected function to throw an exception."; + return result; + } + + if (arguments.length == 1) { + result.pass = true; + result.message = "Expected function not to throw, but it threw " + j$.pp(thrown) + "."; + + return result; + } + + if (util.equals(thrown, expected)) { + result.pass = true; + result.message = "Expected function not to throw " + j$.pp(expected) + "."; + } else { + result.message = "Expected function to throw " + j$.pp(expected) + ", but it threw " + j$.pp(thrown) + "."; + } + + return result; + } + }; + } + + return toThrow; +}; + +getJasmineRequireObj().toThrowError = function(j$) { + function toThrowError (util) { + return { + compare: function(actual) { + var threw = false, + thrown, + errorType, + message, + regexp, + name, + constructorName; + + if (typeof actual != "function") { + throw new Error("Actual is not a Function"); + } + + extractExpectedParams.apply(null, arguments); + + try { + actual(); + } catch (e) { + threw = true; + thrown = e; + } + + if (!threw) { + return fail("Expected function to throw an Error."); + } + + if (!(thrown instanceof Error)) { + return fail("Expected function to throw an Error, but it threw " + thrown + "."); + } + + if (arguments.length == 1) { + return pass("Expected function not to throw an Error, but it threw " + fnNameFor(thrown) + "."); + } + + if (errorType) { + name = fnNameFor(errorType); + constructorName = fnNameFor(thrown.constructor); + } + + if (errorType && message) { + if (thrown.constructor == errorType && util.equals(thrown.message, message)) { + return pass("Expected function not to throw " + name + " with message \"" + message + "\"."); + } else { + return fail("Expected function to throw " + name + " with message \"" + message + + "\", but it threw " + constructorName + " with message \"" + thrown.message + "\"."); + } + } + + if (errorType && regexp) { + if (thrown.constructor == errorType && regexp.test(thrown.message)) { + return pass("Expected function not to throw " + name + " with message matching " + regexp + "."); + } else { + return fail("Expected function to throw " + name + " with message matching " + regexp + + ", but it threw " + constructorName + " with message \"" + thrown.message + "\"."); + } + } + + if (errorType) { + if (thrown.constructor == errorType) { + return pass("Expected function not to throw " + name + "."); + } else { + return fail("Expected function to throw " + name + ", but it threw " + constructorName + "."); + } + } + + if (message) { + if (thrown.message == message) { + return pass("Expected function not to throw an exception with message " + j$.pp(message) + "."); + } else { + return fail("Expected function to throw an exception with message " + j$.pp(message) + + ", but it threw an exception with message " + j$.pp(thrown.message) + "."); + } + } + + if (regexp) { + if (regexp.test(thrown.message)) { + return pass("Expected function not to throw an exception with a message matching " + j$.pp(regexp) + "."); + } else { + return fail("Expected function to throw an exception with a message matching " + j$.pp(regexp) + + ", but it threw an exception with message " + j$.pp(thrown.message) + "."); + } + } + + function fnNameFor(func) { + return func.name || func.toString().match(/^\s*function\s*(\w*)\s*\(/)[1]; + } + + function pass(notMessage) { + return { + pass: true, + message: notMessage + }; + } + + function fail(message) { + return { + pass: false, + message: message + }; + } + + function extractExpectedParams() { + if (arguments.length == 1) { + return; + } + + if (arguments.length == 2) { + var expected = arguments[1]; + + if (expected instanceof RegExp) { + regexp = expected; + } else if (typeof expected == "string") { + message = expected; + } else if (checkForAnErrorType(expected)) { + errorType = expected; + } + + if (!(errorType || message || regexp)) { + throw new Error("Expected is not an Error, string, or RegExp."); + } + } else { + if (checkForAnErrorType(arguments[1])) { + errorType = arguments[1]; + } else { + throw new Error("Expected error type is not an Error."); + } + + if (arguments[2] instanceof RegExp) { + regexp = arguments[2]; + } else if (typeof arguments[2] == "string") { + message = arguments[2]; + } else { + throw new Error("Expected error message is not a string or RegExp."); + } + } + } + + function checkForAnErrorType(type) { + if (typeof type !== "function") { + return false; + } + + var Surrogate = function() {}; + Surrogate.prototype = type.prototype; + return (new Surrogate()) instanceof Error; + } + } + }; + } + + return toThrowError; +}; + +getJasmineRequireObj().version = function() { + return "2.0.0"; +}; diff --git a/jstest/lib/jasmine_favicon.png b/jstest/lib/jasmine_favicon.png new file mode 100644 index 0000000000000000000000000000000000000000..3562e278f108d0f6a918d198f21e055e601c7e71 GIT binary patch literal 2057 zcmV+k2=@1hP)&ijKvfTC%$Wid1aFR2$ii+A1Wx?rx%@ z&1NNpLfWZ1g6QrB21_kem?Ba;0|mzpS}7H&g-YZnkRK=6?7O$$ecAi=Wg&q(^Y)(K zd(J)Q+;i`el#*S{2tOPiRoYFrGh&sD(R7k*sw7qlU9~_bzQZKtej(Y8DaO^i{0YBx zG3!ESWCvpN8mF*~^%OCB2n&wAm_;G;1?1QEc|9wiUjMXmZrCxb1K~i}TPpZ6?D&_^ zE*y4+kr}5YqouXUy0ycJo!5b;s>Z1X7ww46j66k4lKd-4+6~@8D0jj^j&C9fnkG5j z%A~vwI+Ulk#ZxFAs99%r<Qy@CSV`29g zvVeMmZ`A$MrNf$h;R;UlDokAO(20Fm$i&T$Cd?j9vlmcE%j}jy7~crcF^)p|RVZ11 zU%qiA&n4R`INPH>W{EaMbB`xd<|kbwXQBqyJ9NpW_cQ*Ag9C6Ju!=fO;3|KEgP0ji z)`b-kt7^P4?vFg1%z`J*qYRG0}f-F>a~5NhU4M@h1(m@oRE#%%}UzHm7_eL@p4 zZwe*qBN{K1{1EsHVu6NxHEp=LIS_dSJ>srWY3rDhF-=pPEwO%#viwl%aAGUk?hTJo z_+SKxegW8<6*%J|&*Cg>PmgVU<`foq5CzlLQWY+9^W3n?JCpPRXJT&kTjp1qXTEIq zNx>clZZ{`Y;5}Zhij?uyfp9T)_kb2g-eWb1XHLK%g~MSdMTp3|lF7CV=dUFLNkhx) zS=A8d{0-=tOH$@PG+oQqrs_YyNpey_#OzIa(#*IMbzGcPNE>9VQE-z#`mNFfd~?hn z*+@j~WvpU%_emKkoycA|%nldpaA`y7(Or|dkUK-E<*RaSn^$4&JL5kJR|GeiXC(1f zVq$()XIir_dJRV_uGEXb`(5_#LEwVO2Kt9-`ZQ;h7G zqk-%^2^!T{blcbo{I}>0KZKLlGvYT4q`gaeN6`*!CJEWO_)6Tmg3;hw$f4?@nul7r z&I9D@If6W>V7o5J*hfrG2C8nnAp?ru;9y5{x*B#~I7BFibdZq$(I|YdZP+-MQ}ywYVUCG z0U>cK(4ZzDscPViUI@FKs9`m?1fIJj=8OCk;r&y;LG9#m+3t$Q(hz9z*9`)9i1!qiu0pftFJEZ@$WPPVR=m5jv9Z-7rg3{7vVKRiDi3GKlM2QBvw-E zOY@(-RMXJXZv%|BpB<8|RuF1{;n6E&n7S2=F9u=%CrdhuGI~TmWN;UZLkDy=37JDh6DGf|WNinY+-QD@5Sv+L9CiBM zVBZG?D+QDFzeZW!1Z;CxiFiuOxo36nGhr+QZHo|mdTnyK-K^BbBt1NZC|=>n6FpwN zw%Sga^CnbRZ|O&T8w}3Uf|9Y{ca!*;df8~2d&W6`1qJ+mFj3#;rfXR3+&0db3co$D zpH?{cbB)^(P>9-rH{+QL2mYm*2A4|1)Y1G+xf6*p7uN^kSGTD6f~2w}ev5{_dg=0aX->Kcd_4M$=;9?x? zYV20E-3tyly%$2OyHAL9=3mUrND3Epz^&XIh<*dF+e%ze-^BTxirwLSODTw*L>`Xd nulYW~`SvFhb)UKQMuPtji`gObi$ZJx00000NkvXXu0mjf%0~0J literal 0 HcmV?d00001 diff --git a/jstest/lib/mock-ajax.js b/jstest/lib/mock-ajax.js new file mode 100644 index 0000000000..aac8c98863 --- /dev/null +++ b/jstest/lib/mock-ajax.js @@ -0,0 +1,367 @@ +/* + +Jasmine-Ajax : a set of helpers for testing AJAX requests under the Jasmine +BDD framework for JavaScript. + +http://github.com/pivotal/jasmine-ajax + +Jasmine Home page: http://pivotal.github.com/jasmine + +Copyright (c) 2008-2013 Pivotal Labs + +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. + +*/ + +(function() { + function extend(destination, source, propertiesToSkip) { + propertiesToSkip = propertiesToSkip || []; + for (var property in source) { + if (!arrayContains(propertiesToSkip, property)) { + destination[property] = source[property]; + } + } + return destination; + } + + function arrayContains(arr, item) { + for (var i = 0; i < arr.length; i++) { + if (arr[i] === item) { + return true; + } + } + return false; + } + + function MockAjax(global) { + var requestTracker = new RequestTracker(), + stubTracker = new StubTracker(), + paramParser = new ParamParser(), + realAjaxFunction = global.XMLHttpRequest, + mockAjaxFunction = fakeRequest(requestTracker, stubTracker, paramParser); + + this.install = function() { + global.XMLHttpRequest = mockAjaxFunction; + }; + + this.uninstall = function() { + global.XMLHttpRequest = realAjaxFunction; + + this.stubs.reset(); + this.requests.reset(); + paramParser.reset(); + }; + + this.stubRequest = function(url, data) { + var stub = new RequestStub(url, data); + stubTracker.addStub(stub); + return stub; + }; + + this.withMock = function(closure) { + this.install(); + try { + closure(); + } finally { + this.uninstall(); + } + }; + + this.addCustomParamParser = function(parser) { + paramParser.add(parser); + }; + + this.requests = requestTracker; + this.stubs = stubTracker; + } + + function StubTracker() { + var stubs = []; + + this.addStub = function(stub) { + stubs.push(stub); + }; + + this.reset = function() { + stubs = []; + }; + + this.findStub = function(url, data) { + for (var i = stubs.length - 1; i >= 0; i--) { + var stub = stubs[i]; + if (stub.matches(url, data)) { + return stub; + } + } + }; + } + + function ParamParser() { + var defaults = [ + { + test: function(xhr) { + return /^application\/json/.test(xhr.contentType()); + }, + parse: function jsonParser(paramString) { + return JSON.parse(paramString); + } + }, + { + test: function(xhr) { + return true; + }, + parse: function naiveParser(paramString) { + var data = {}; + var params = paramString.split('&'); + + for (var i = 0; i < params.length; ++i) { + var kv = params[i].replace(/\+/g, ' ').split('='); + var key = decodeURIComponent(kv[0]); + data[key] = data[key] || []; + data[key].push(decodeURIComponent(kv[1])); + } + return data; + } + } + ]; + var paramParsers = []; + + this.add = function(parser) { + paramParsers.unshift(parser); + }; + + this.findParser = function(xhr) { + for(var i in paramParsers) { + var parser = paramParsers[i]; + if (parser.test(xhr)) { + return parser; + } + } + }; + + this.reset = function() { + paramParsers = []; + for(var i in defaults) { + paramParsers.push(defaults[i]); + } + }; + + this.reset(); + } + + function fakeRequest(requestTracker, stubTracker, paramParser) { + function FakeXMLHttpRequest() { + requestTracker.track(this); + this.requestHeaders = {}; + } + + var iePropertiesThatCannotBeCopied = ['responseBody', 'responseText', 'responseXML', 'status', 'statusText', 'responseTimeout']; + extend(FakeXMLHttpRequest.prototype, new window.XMLHttpRequest(), iePropertiesThatCannotBeCopied); + extend(FakeXMLHttpRequest.prototype, { + open: function() { + this.method = arguments[0]; + this.url = arguments[1]; + this.username = arguments[3]; + this.password = arguments[4]; + this.readyState = 1; + this.onreadystatechange(); + }, + + setRequestHeader: function(header, value) { + this.requestHeaders[header] = value; + }, + + abort: function() { + this.readyState = 0; + this.status = 0; + this.statusText = "abort"; + this.onreadystatechange(); + }, + + readyState: 0, + + onload: function() { + }, + + onreadystatechange: function(isTimeout) { + }, + + status: null, + + send: function(data) { + this.params = data; + this.readyState = 2; + this.onreadystatechange(); + + var stub = stubTracker.findStub(this.url, data); + if (stub) { + this.response(stub); + } + }, + + contentType: function() { + for (var header in this.requestHeaders) { + if (header.toLowerCase() === 'content-type') { + return this.requestHeaders[header]; + } + } + }, + + data: function() { + if (!this.params) { + return {}; + } + + return paramParser.findParser(this).parse(this.params); + }, + + getResponseHeader: function(name) { + return this.responseHeaders[name]; + }, + + getAllResponseHeaders: function() { + var responseHeaders = []; + for (var i in this.responseHeaders) { + if (this.responseHeaders.hasOwnProperty(i)) { + responseHeaders.push(i + ': ' + this.responseHeaders[i]); + } + } + return responseHeaders.join('\r\n'); + }, + + responseText: null, + + response: function(response) { + this.status = response.status; + this.statusText = response.statusText || ""; + this.responseText = response.responseText || ""; + this.readyState = 4; + this.responseHeaders = response.responseHeaders || + {"Content-Type": response.contentType || "application/json" }; + + this.onload(); + this.onreadystatechange(); + }, + + responseTimeout: function() { + this.readyState = 4; + jasmine.clock().tick(30000); + this.onreadystatechange('timeout'); + } + }); + + return FakeXMLHttpRequest; + } + + function RequestTracker() { + var requests = []; + + this.track = function(request) { + requests.push(request); + }; + + this.first = function() { + return requests[0]; + }; + + this.count = function() { + return requests.length; + }; + + this.reset = function() { + requests = []; + }; + + this.mostRecent = function() { + return requests[requests.length - 1]; + }; + + this.at = function(index) { + return requests[index]; + }; + + this.filter = function(url_to_match) { + if (requests.length == 0) return []; + var matching_requests = []; + + for (var i = 0; i < requests.length; i++) { + if (url_to_match instanceof RegExp && + url_to_match.test(requests[i].url)) { + matching_requests.push(requests[i]); + } else if (url_to_match instanceof Function && + url_to_match(requests[i])) { + matching_requests.push(requests[i]); + } else { + if (requests[i].url == url_to_match) { + matching_requests.push(requests[i]); + } + } + } + + return matching_requests; + }; + } + + function RequestStub(url, stubData) { + var normalizeQuery = function(query) { + return query ? query.split('&').sort().join('&') : undefined; + }; + + if (url instanceof RegExp) { + this.url = url; + this.query = undefined; + } else { + var split = url.split('?'); + this.url = split[0]; + this.query = split.length > 1 ? normalizeQuery(split[1]) : undefined; + } + + this.data = normalizeQuery(stubData); + + this.andReturn = function(options) { + this.status = options.status || 200; + + this.contentType = options.contentType; + this.responseText = options.responseText; + }; + + this.matches = function(fullUrl, data) { + var matches = false; + fullUrl = fullUrl.toString(); + if (this.url instanceof RegExp) { + matches = this.url.test(fullUrl); + } else { + var urlSplit = fullUrl.split('?'), + url = urlSplit[0], + query = urlSplit[1]; + matches = this.url === url && this.query === normalizeQuery(query); + } + return matches && (!this.data || this.data === normalizeQuery(data)); + }; + } + + if (typeof window === "undefined" && typeof exports === "object") { + exports.MockAjax = MockAjax; + jasmine.Ajax = new MockAjax(exports); + } else { + window.MockAjax = MockAjax; + jasmine.Ajax = new MockAjax(window); + } +}()); diff --git a/jstest/spec-runner.js b/jstest/spec-runner.js new file mode 100644 index 0000000000..96bc053133 --- /dev/null +++ b/jstest/spec-runner.js @@ -0,0 +1,86 @@ +require.config({ + + baseUrl: '..', + + paths: { + jquery: ['vendor/assets/javascripts/jquery'], + underscore: ['vendor/assets/javascripts/underscore'], + backbone: ['vendor/assets/javascripts/backbone'], + mps: ['vendor/assets/javascripts/minpubsub'], + backbonequeryparams: ['vendor/assets/javascripts/backbone.queryparams'], + gmap: ['app/assets/javascripts/map/gmap'], + d3: ['vendor/assets/javascripts/d3'], + backbone_cartodb: ['vendor/assets/javascripts/backbone.cartodb'], + cartodb: ['vendor/assets/javascripts/cartodb'], + store: ['vendor/assets/javascripts/store'], + text: ['vendor/assets/javascripts/text'], + Class: ['vendor/assets/javascripts/class'], + uri: ['vendor/assets/javascripts/uri'], + app: ['app/assets/javascripts/map/app'], + nsa: ['app/assets/javascripts/map/nsa'], + moment: ['vendor/assets/javascripts/moment'], + analysis: ['app/assets/javascripts/map/analysis'], + router: ['app/assets/javascripts/map/router'], + mediator: ['app/assets/javascripts/map/mediator'], + presenter: ['app/assets/javascripts/map/presenter'], + views: ['app/assets/javascripts/map/views'], + models: ['app/assets/javascripts/map/models'], + collections: ['app/assets/javascripts/map/collections'], + + spec: ['../jstest/spec'], + helpers: ['../jstest/helpers'], + jasmine: ['../jstest/lib/jasmine'], + jasmine_html: ['../jstest/lib/jasmine-html'], + jasmine_boot: ['../jstest/lib/boot'], + mock_ajax: ['../jstest/lib/mock-ajax'] + }, + + shim: { + underscore: { + exports: '_' + }, + backbone: { + deps: ['jquery', 'underscore'], + exports: 'Backbone', + }, + backbone_cartodb: { + deps: ['underscore', 'backbone'], + exports: 'backbone_cartodb' + }, + backbonequeryparams: { + deps: ['backbone', 'underscore'], + exports: 'backbonequeryparams' + }, + uri: { + exports: 'UriTemplate', + }, + Class: { + exports: 'Class', + }, + app: { + deps: ['mps', 'Class'], + exports: 'app' + }, + user: { + deps: ['Class'] + }, + mps: { + deps: ['jquery', 'underscore'], + exports: 'mps', + init: function(foo) { + var mps = { + subscribe: window.subscribe, + unsubscribe: window.unsubscribe, + publish: window.publish + }; + return mps; + } + } + } +}); + +require( +['spec/analysis_spec', 'mock_ajax'], +function(){ + // NOOP +}); \ No newline at end of file diff --git a/jstest/spec/analysis_spec.js b/jstest/spec/analysis_spec.js new file mode 100644 index 0000000000..3d43e27ca8 --- /dev/null +++ b/jstest/spec/analysis_spec.js @@ -0,0 +1,68 @@ +define([ + 'app', + 'analysis', + 'mps', + 'underscore', + 'helpers/api_responses' +], function(app, analysis, mps, _) { + + describe("The analysis module", function() { + var apis = { + global: "http://{0}/forest-change/{1}{?period,geojson,download,bust,dev}", + national: "http://{0}/forest-change/{1}/admin{/iso}{?period,download,bust,dev}", + subnational: "http://{0}/forest-change/{1}/admin{/iso}{/id1}{?period,download,bust,dev}", + use: "http://{0}/forest-change/{1}/use/{/name}{/id}{?period,download,bust,dev}", + wdpa: "http://{0}/forest-change/{1}/wdpa/{/id}{?period,download,bust,dev}" + }; + + it("is not null", function() { + expect(analysis).not.toBe(null); + }); + + it("correctly classifies apis", function() { + var template = analysis.get_uritemplate({iso: 'bra'}); + var url = analysis.get_url({layerName: 'forma-alerts', iso: 'bra'}); + expect(template).toBe(apis.national); + expect(url).toBe('http://beta.gfw-apis.appspot.com/forest-change/forma-alerts/admin/bra'); + }); + }); + + describe("Run analysis via mps.publish(analysis/get)", function() { + var requeat = null; + + beforeEach(function() { + jasmine.Ajax.install(); + + mps.publish('analysis/get', [{layerName: 'forma-alerts', iso: 'bra'}]); + + request = jasmine.Ajax.requests.mostRecent(); + expect(request.url).toBe('http://beta.gfw-apis.appspot.com/forest-change/forma-alerts/admin/bra'); + expect(request.method).toBe('POST'); + expect(request.data()).toEqual({}); + }); + + describe("Check mps.publish(analysis/get-results)", function() { + var callback = null; + + beforeEach(function(done) { + callback = { + spy: function(response) { + done(); + } + }; + spyOn(callback, 'spy').and.callThrough(); + mps.subscribe('analysis/get-results', callback.spy); + request.response(ApiResponse.forma_alerts.iso.success); + }); + + it("fires subscriber callback with correct API response", function() { + var response = null; + var expected = JSON.parse(ApiResponse.forma_alerts.iso.success.responseText); + + expect(callback.spy).toHaveBeenCalled(); + response = callback.spy.calls.mostRecent().args[0]; + expect(response).toEqual(expected); + }); + }); + }); +}); \ No newline at end of file diff --git a/jstest/spec/api_responses/wdpa_success.js b/jstest/spec/api_responses/wdpa_success.js new file mode 100644 index 0000000000..d71bfb7cc1 --- /dev/null +++ b/jstest/spec/api_responses/wdpa_success.js @@ -0,0 +1,11 @@ + + var TestResponses = { + forma_alerts: { + wdpa: { + success: { + status: 200, + responseText: '{"coverage": "Humid tropical forest biome", "description": "Alerts where forest disturbances have likely occurred.", "id": "forma-alerts", "name": "FORMA", "resolution": "500 x 500 meters", "source": "MODIS", "timescale": "January 2006 to present", "units": "Alerts", "updates": "16 day", "value": 847, "wdpaid": "8950"}' + } + } + } + }; diff --git a/jstest/spec/api_responses/wdpa_success.json b/jstest/spec/api_responses/wdpa_success.json new file mode 100644 index 0000000000..ac5a13ef22 --- /dev/null +++ b/jstest/spec/api_responses/wdpa_success.json @@ -0,0 +1,2 @@ + +response = '{"coverage": "Humid tropical forest biome", "description": "Alerts where forest disturbances have likely occurred.", "id": "forma-alerts", "name": "FORMA", "resolution": "500 x 500 meters", "source": "MODIS", "timescale": "January 2006 to present", "units": "Alerts", "updates": "16 day", "value": 847, "wdpaid": "8950"}'; \ No newline at end of file diff --git a/jstest/spec/app_spec.js b/jstest/spec/app_spec.js new file mode 100644 index 0000000000..04cfea6a53 --- /dev/null +++ b/jstest/spec/app_spec.js @@ -0,0 +1,18 @@ +define([ + 'app', + 'underscore', + 'backbone' +], function(app, _, Backbone) { + + describe("The app module", function() { + + it("is not null", function() { + expect(app).not.toBe(null); + }); + + it("has defined capitalize() and parseUrl() mixins", function() { + expect(_.capitalize).not.toBe(null); + expect(_.parseUrl).not.toBe(null); + }); + }); +}); \ No newline at end of file diff --git a/jstest/spec/router_spec.js b/jstest/spec/router_spec.js new file mode 100644 index 0000000000..ef8a7ad96d --- /dev/null +++ b/jstest/spec/router_spec.js @@ -0,0 +1,31 @@ +define([ + 'router', + 'underscore', + 'backbone', + 'mps' +], function(Router, _, Backbone, mps) { + + describe("The router module", function() { + + var router = null; + + beforeEach(function() { + var mockApp = {}; + router = new Router(mockApp); + }); + + it("is not null", function() { + expect(router).not.toBe(null); + }); + + it("handles navigate event", function() { + mps.publish('navigate', [{path: 'foo/bar/baz'}]); + expect(router.path).toBe('foo/bar/baz'); + }); + + it("handles map route", function() { + router.map('foo', 'bar', 'baz'); + expect(router.mapView).not.toBe(null); + }); + }); +}); \ No newline at end of file diff --git a/testem.yml b/testem.yml new file mode 100644 index 0000000000..f97e743041 --- /dev/null +++ b/testem.yml @@ -0,0 +1,9 @@ +framework: jasmine2 +test_page: jstest/index.html +src_files: +- app/assets/javascripts/map/** +- vendor/assets/javascripts/** +- jstest/** +launch_in_dev: +- Chrome +- Firefox \ No newline at end of file diff --git a/vendor/assets/javascripts/uri.js b/vendor/assets/javascripts/uri.js new file mode 100644 index 0000000000..2a5901e71b --- /dev/null +++ b/vendor/assets/javascripts/uri.js @@ -0,0 +1,403 @@ +var UriTemplate = (function () { + + var uriTemplateGlobalModifiers = { + "+": true, + "#": true, + ".": true, + "/": true, + ";": true, + "?": true, + "&": true + }; + var uriTemplateSuffices = { + "*": true + }; + + function notReallyPercentEncode(string) { + return encodeURI(string).replace(/%25[0-9][0-9]/g, function (doubleEncoded) { + return "%" + doubleEncoded.substring(3); + }); + } + + function uriTemplateSubstitution(spec) { + var modifier = ""; + if (uriTemplateGlobalModifiers[spec.charAt(0)]) { + modifier = spec.charAt(0); + spec = spec.substring(1); + } + var separator = ""; + var prefix = ""; + var shouldEscape = true; + var showVariables = false; + var trimEmptyString = false; + if (modifier == '+') { + shouldEscape = false; + } else if (modifier == ".") { + prefix = "."; + separator = "."; + } else if (modifier == "/") { + prefix = "/"; + separator = "/"; + } else if (modifier == '#') { + prefix = "#"; + shouldEscape = false; + } else if (modifier == ';') { + prefix = ";"; + separator = ";", + showVariables = true; + trimEmptyString = true; + } else if (modifier == '?') { + prefix = "?"; + separator = "&", + showVariables = true; + } else if (modifier == '&') { + prefix = "&"; + separator = "&", + showVariables = true; + } + + var varNames = []; + var varList = spec.split(","); + var varSpecs = []; + var varSpecMap = {}; + for (var i = 0; i < varList.length; i++) { + var varName = varList[i]; + var truncate = null; + if (varName.indexOf(":") != -1) { + var parts = varName.split(":"); + varName = parts[0]; + truncate = parseInt(parts[1]); + } + var suffices = {}; + while (uriTemplateSuffices[varName.charAt(varName.length - 1)]) { + suffices[varName.charAt(varName.length - 1)] = true; + varName = varName.substring(0, varName.length - 1); + } + var varSpec = { + truncate: truncate, + name: varName, + suffices: suffices + }; + varSpecs.push(varSpec); + varSpecMap[varName] = varSpec; + varNames.push(varName); + } + var subFunction = function (valueFunction) { + var result = ""; + var startIndex = 0; + for (var i = 0; i < varSpecs.length; i++) { + var varSpec = varSpecs[i]; + var value = valueFunction(varSpec.name); + if (value == null || (Array.isArray(value) && value.length == 0) || (typeof value == 'object' && Object.keys(value).length == 0)) { + startIndex++; + continue; + } + if (i == startIndex) { + result += prefix; + } else { + result += (separator || ","); + } + if (Array.isArray(value)) { + if (showVariables) { + result += varSpec.name + "="; + } + for (var j = 0; j < value.length; j++) { + if (j > 0) { + result += varSpec.suffices['*'] ? (separator || ",") : ","; + if (varSpec.suffices['*'] && showVariables) { + result += varSpec.name + "="; + } + } + result += shouldEscape ? encodeURIComponent(value[j]).replace(/!/g, "%21") : notReallyPercentEncode(value[j]); + } + } else if (typeof value == "object") { + if (showVariables && !varSpec.suffices['*']) { + result += varSpec.name + "="; + } + var first = true; + for (var key in value) { + if (!first) { + result += varSpec.suffices['*'] ? (separator || ",") : ","; + } + first = false; + result += shouldEscape ? encodeURIComponent(key).replace(/!/g, "%21") : notReallyPercentEncode(key); + result += varSpec.suffices['*'] ? '=' : ","; + result += shouldEscape ? encodeURIComponent(value[key]).replace(/!/g, "%21") : notReallyPercentEncode(value[key]); + } + } else { + if (showVariables) { + result += varSpec.name; + if (!trimEmptyString || value != "") { + result += "="; + } + } + if (varSpec.truncate != null) { + value = value.substring(0, varSpec.truncate); + } + result += shouldEscape ? encodeURIComponent(value).replace(/!/g, "%21"): notReallyPercentEncode(value); + } + } + return result; + }; + var guessFunction = function (stringValue, resultObj) { + if (prefix) { + if (stringValue.substring(0, prefix.length) == prefix) { + stringValue = stringValue.substring(prefix.length); + } else { + return null; + } + } + if (varSpecs.length == 1 && varSpecs[0].suffices['*']) { + var varSpec = varSpecs[0]; + var varName = varSpec.name; + var arrayValue = varSpec.suffices['*'] ? stringValue.split(separator || ",") : [stringValue]; + var hasEquals = (shouldEscape && stringValue.indexOf('=') != -1); // There's otherwise no way to distinguish between "{value*}" for arrays and objects + for (var i = 1; i < arrayValue.length; i++) { + var stringValue = arrayValue[i]; + if (hasEquals && stringValue.indexOf('=') == -1) { + // Bit of a hack - if we're expecting "=" for key/value pairs, and values can't contain "=", then assume a value has been accidentally split + arrayValue[i - 1] += (separator || ",") + stringValue; + arrayValue.splice(i, 1); + i--; + } + } + for (var i = 0; i < arrayValue.length; i++) { + var stringValue = arrayValue[i]; + if (shouldEscape && stringValue.indexOf('=') != -1) { + hasEquals = true; + } + var innerArrayValue = stringValue.split(","); + for (var j = 0; j < innerArrayValue.length; j++) { + if (shouldEscape) { + innerArrayValue[j] = decodeURIComponent(innerArrayValue[j]); + } + } + if (innerArrayValue.length == 1) { + arrayValue[i] = innerArrayValue[0]; + } else { + arrayValue[i] = innerArrayValue; + } + } + + if (showVariables || hasEquals) { + var objectValue = resultObj[varName] || {}; + for (var j = 0; j < arrayValue.length; j++) { + var innerValue = stringValue; + if (typeof arrayValue[j] == "string") { + var stringValue = arrayValue[j]; + var innerVarName = stringValue.split("=", 1)[0]; + var stringValue = stringValue.substring(innerVarName.length + 1); + innerValue = stringValue; + } else { + var stringValue = arrayValue[j][0]; + var innerVarName = stringValue.split("=", 1)[0]; + var stringValue = stringValue.substring(innerVarName.length + 1); + arrayValue[j][0] = stringValue; + innerValue = arrayValue[j]; + } + if (objectValue[innerVarName] !== undefined) { + if (Array.isArray(objectValue[innerVarName])) { + objectValue[innerVarName].push(innerValue); + } else { + objectValue[innerVarName] = [objectValue[innerVarName], innerValue]; + } + } else { + objectValue[innerVarName] = innerValue; + } + } + if (Object.keys(objectValue).length == 1 && objectValue[varName] !== undefined) { + resultObj[varName] = objectValue[varName]; + } else { + resultObj[varName] = objectValue; + } + } else { + if (resultObj[varName] !== undefined) { + if (Array.isArray(resultObj[varName])) { + resultObj[varName] = resultObj[varName].concat(arrayValue); + } else { + resultObj[varName] = [resultObj[varName]].concat(arrayValue); + } + } else { + if (arrayValue.length == 1 && !varSpec.suffices['*']) { + resultObj[varName] = arrayValue[0]; + } else { + resultObj[varName] = arrayValue; + } + } + } + } else { + var arrayValue = (varSpecs.length == 1) ? [stringValue] : stringValue.split(separator || ","); + var specIndexMap = {}; + for (var i = 0; i < arrayValue.length; i++) { + // Try from beginning + var firstStarred = 0; + for (; firstStarred < varSpecs.length - 1 && firstStarred < i; firstStarred++) { + if (varSpecs[firstStarred].suffices['*']) { + break; + } + } + if (firstStarred == i) { + // The first [i] of them have no "*" suffix + specIndexMap[i] = i; + continue; + } else { + // Try from the end + for (var lastStarred = varSpecs.length - 1; lastStarred > 0 && (varSpecs.length - lastStarred) < (arrayValue.length - i); lastStarred--) { + if (varSpecs[lastStarred].suffices['*']) { + break; + } + } + if ((varSpecs.length - lastStarred) == (arrayValue.length - i)) { + // The last [length - i] of them have no "*" suffix + specIndexMap[i] = lastStarred; + continue; + } + } + // Just give up and use the first one + specIndexMap[i] = firstStarred; + } + for (var i = 0; i < arrayValue.length; i++) { + var stringValue = arrayValue[i]; + var innerArrayValue = stringValue.split(","); + + if (showVariables) { + var stringValue = innerArrayValue[0]; // using innerArrayValue + var varName = stringValue.split("=", 1)[0]; + var stringValue = stringValue.substring(varName.length + 1); + innerArrayValue[0] = stringValue; + var varSpec = varSpecMap[varName] || varSpecs[0]; + } else { + var varSpec = varSpecs[specIndexMap[i]]; + var varName = varSpec.name; + } + + for (var j = 0; j < innerArrayValue.length; j++) { + if (shouldEscape) { + innerArrayValue[j] = decodeURIComponent(innerArrayValue[j]); + } + } + + if ((showVariables || varSpec.suffices['*'])&& resultObj[varName] !== undefined) { + if (Array.isArray(resultObj[varName])) { + resultObj[varName] = resultObj[varName].concat(innerArrayValue); + } else { + resultObj[varName] = [resultObj[varName]].concat(innerArrayValue); + } + } else { + if (innerArrayValue.length == 1 && !varSpec.suffices['*']) { + resultObj[varName] = innerArrayValue[0]; + } else { + resultObj[varName] = innerArrayValue; + } + } + } + } + }; + subFunction.varNames = varNames; + return { + prefix: prefix, + substitution: subFunction, + unSubstitution: guessFunction + }; + } + + function UriTemplate(template) { + if (!(this instanceof UriTemplate)) { + return new UriTemplate(template); + } + var parts = template.split("{"); + var textParts = [parts.shift()]; + var prefixes = []; + var substitutions = []; + var unSubstitutions = []; + var varNames = []; + while (parts.length > 0) { + var part = parts.shift(); + var spec = part.split("}")[0]; + var remainder = part.substring(spec.length + 1); + var funcs = uriTemplateSubstitution(spec); + substitutions.push(funcs.substitution); + unSubstitutions.push(funcs.unSubstitution); + prefixes.push(funcs.prefix); + textParts.push(remainder); + varNames = varNames.concat(funcs.substitution.varNames); + } + this.fill = function (valueFunction) { + var result = textParts[0]; + for (var i = 0; i < substitutions.length; i++) { + var substitution = substitutions[i]; + result += substitution(valueFunction); + result += textParts[i + 1]; + } + return result; + }; + this.fromUri = function (substituted) { + var result = {}; + for (var i = 0; i < textParts.length; i++) { + var part = textParts[i]; + if (substituted.substring(0, part.length) !== part) { + return undefined; + } + substituted = substituted.substring(part.length); + if (i >= textParts.length - 1) { + if (substituted == "") { + break; + } else { + return undefined; + } + } + var nextPart = textParts[i + 1]; + var offset = i; + while (true) { + if (offset == textParts.length - 2) { + var endPart = substituted.substring(substituted.length - nextPart.length); + if (endPart !== nextPart) { + return undefined; + } + var stringValue = substituted.substring(0, substituted.length - nextPart.length); + substituted = endPart; + } else if (nextPart) { + var nextPartPos = substituted.indexOf(nextPart); + var stringValue = substituted.substring(0, nextPartPos); + substituted = substituted.substring(nextPartPos); + } else if (prefixes[offset + 1]) { + var nextPartPos = substituted.indexOf(prefixes[offset + 1]); + var stringValue = substituted.substring(0, nextPartPos); + substituted = substituted.substring(nextPartPos); + } else if (textParts.length > offset + 2) { + // If the separator between this variable and the next is blank (with no prefix), continue onwards + offset++; + nextPart = textParts[offset + 1]; + continue; + } else { + var stringValue = substituted; + substituted = ""; + } + break; + } + unSubstitutions[i](stringValue, result); + } + return result; + } + this.varNames = varNames; + this.template = template; + } + UriTemplate.prototype = { + toString: function () { + return this.template; + }, + fillFromObject: function (obj) { + return this.fill(function (varName) { + return obj[varName]; + }); + } + }; + + if (typeof module != 'undefined') { + module.exports = UriTemplate; + } + if (this) { + this.UriTemplate = UriTemplate; + } + return UriTemplate; +}).call(this); \ No newline at end of file From 6e2d87396a92064c2de565e635d1f1a61edf21eb Mon Sep 17 00:00:00 2001 From: Adrian Date: Wed, 18 Jun 2014 11:58:48 -0700 Subject: [PATCH 096/823] templates files --- .../javascripts/map/views/analysis.html | 200 +++++++++ app/assets/javascripts/map/views/circle.html | 30 ++ app/assets/javascripts/map/views/filter.html | 55 +++ app/assets/javascripts/map/views/legend.html | 141 +++++++ app/assets/javascripts/map/views/legend.js | 389 ++++++++++++++++++ app/assets/javascripts/map/views/search.html | 5 + app/assets/javascripts/map/views/share.html | 27 ++ 7 files changed, 847 insertions(+) create mode 100644 app/assets/javascripts/map/views/analysis.html create mode 100644 app/assets/javascripts/map/views/circle.html create mode 100644 app/assets/javascripts/map/views/filter.html create mode 100644 app/assets/javascripts/map/views/legend.html create mode 100644 app/assets/javascripts/map/views/legend.js create mode 100644 app/assets/javascripts/map/views/search.html create mode 100644 app/assets/javascripts/map/views/share.html diff --git a/app/assets/javascripts/map/views/analysis.html b/app/assets/javascripts/map/views/analysis.html new file mode 100644 index 0000000000..bec722e459 --- /dev/null +++ b/app/assets/javascripts/map/views/analysis.html @@ -0,0 +1,200 @@ + + + + + + + + + \ No newline at end of file diff --git a/app/assets/javascripts/map/views/circle.html b/app/assets/javascripts/map/views/circle.html new file mode 100644 index 0000000000..4a02cf2c04 --- /dev/null +++ b/app/assets/javascripts/map/views/circle.html @@ -0,0 +1,30 @@ + + + \ No newline at end of file diff --git a/app/assets/javascripts/map/views/filter.html b/app/assets/javascripts/map/views/filter.html new file mode 100644 index 0000000000..fca8755799 --- /dev/null +++ b/app/assets/javascripts/map/views/filter.html @@ -0,0 +1,55 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/app/assets/javascripts/map/views/legend.html b/app/assets/javascripts/map/views/legend.html new file mode 100644 index 0000000000..a21ced9ea5 --- /dev/null +++ b/app/assets/javascripts/map/views/legend.html @@ -0,0 +1,141 @@ + + + + + + + + + diff --git a/app/assets/javascripts/map/views/legend.js b/app/assets/javascripts/map/views/legend.js new file mode 100644 index 0000000000..52615a3a54 --- /dev/null +++ b/app/assets/javascripts/map/views/legend.js @@ -0,0 +1,389 @@ +gfw.ui.model.LegendItem = Backbone.Model.extend(); + +gfw.ui.collection.LegendItems = Backbone.Collection.extend({ + model: gfw.ui.model.LegendItem +}); + +gfw.ui.model.Legend = Backbone.Model.extend({ + defaults: { + hidden: true, + layerCount: 0 + } +}); + +gfw.ui.view.Legend = gfw.ui.view.Widget.extend({ + className: 'legend', + + events: { + 'click .toggle': '_toggleOpen' + }, + + initialize: function() { + _.bindAll(this, 'add', 'replace', 'toggle', 'toggleOpen', 'toggleDraggable', 'onStopDragging', 'addContent', 'removeContent'); + + this.options = _.extend(this.options, this.defaults); + + this.model = new gfw.ui.model.Legend(); + + this.add_related_model(this.model); + + this.model.bind('change:hidden', this.toggle); + this.model.bind('change:closed', this.toggleOpen); + this.model.bind('change:draggable', this.toggleDraggable, this); + + this.model.set('containment', '#map-container .map'); + var template = $('#legend-template').html(); + + this.template = new cdb.core.Template({ + template: template, + type: 'mustache' + }); + + this.categories = {}; + }, + + addScroll: function() { + if (!this.model.get('scrollbar')) { + this.model.set('scrollbar', true); + this.$el.find('.content').jScrollPane(); + this.api = this.$el.find('.content').data('jsp'); + } else { + this.refreshScroll(); + } + }, + + refreshScroll:function() { + if (this.api) this.api.reinitialise(); + }, + + increaseLayerCount: function() { + this.model.set('layerCount', this.model.get('layerCount') + 1); + }, + + decreaseLayerCount: function() { + this.model.set('layerCount', this.model.get('layerCount') - 1); + }, + + show: function(callback) { + if (!showMap) return; + + if (this.model.get('layerCount') === 0) { + this.model.set('hidden', true); + } else { + this.model.set('hidden', false); + } + + if (!this.model.get('closed')) this.resize(); + + callback && callback(); + + return this; + }, + + toggleItemBySlug: function(slug) { + var that = this; + + $('.legend .' + slug).toggle(); + + setTimeout( function() { that.resize(); }, 300); + }, + + toggleItem: function(id, category, category_title, title, slug, category_color, title_color, subevent) { + if(!this.categories[category] || !this.isAdded(id)) { + this.add(id, category, category_title, title, slug, category_color, title_color, subevent); + } else { + this.remove(id); + } + }, + + findItem: function(id) { + var foundItem = null; + + _.each(this.categories, function(c) { + _.each(c.models, function(item) { + if (item.get('cat_id') == id) foundItem = item; + }); + }); + + return foundItem; + }, + + isAdded: function(id) { + var duplicated = false; + + _.each(this.categories, function(c) { + _.each(c.models, function(item) { + if (item.get('cat_id') == id) duplicated = id; + }); + }); + + return duplicated; + }, + + replace: function(id, category, category_title, title, slug, category_color, title_color, subevent) { + var that = this; + + if(this.categories[category]) { + _.each(this.categories[category].models, function(c) { + that.removeContent(category, c.get('cat_id')); + that.categories[category].remove(c); + + that.decreaseLayerCount(); + }); + } + + this.add(id, category, category_title, title, slug, category_color, title_color, subevent); + }, + + removeCategory: function(category) { + var that = this; + + if(this.categories[category]) { + _.each(this.categories[category].models, function(c) { + if (c) { + that.removeContent(category, c.get('cat_id')); + that.categories[category].remove(c); + + that.decreaseLayerCount(); + } + }); + + if (that.categories[category].length == 0) { + delete that.categories[category]; + } + } + + setTimeout( function() { that.resize(); }, 300); + }, + + add: function(id, category, category_title, title, slug, category_color, title_color, subevent) { + var that = this; + + if (_.size(this.categories) && this.isAdded(id)) return; + + this.open(function() { + if (!that.categories[category]) { + that.categories[category] = new gfw.ui.collection.LegendItems; + that.categories[category].bind('add', that.addContent); + } + + var item = new gfw.ui.model.LegendItem({ + cat_id: id, + title: title, + slug: slug, + category: category, + category_title: category_title, + category_color: category_color, + title_color: title_color, + sublayer: subevent ? true : false, + subevent: subevent + }); + + that.categories[category].push( item ); + that.increaseLayerCount(); + if ($('body').hasClass('embed')){ + that.$el.css('left','-99999px'); + that.show(); + that.hide(); + } else { + that.show(); + } + }); + + }, + + remove: function(id) { + var that = this; + var item = this.findItem(id); + + if (!item) return; + + this.open(function() { + var category = item.get('category'); + + if (!that.categories[category]) return; + + that.categories[category].each(function(c) { + if (c && (c.get('cat_id') == id)) { + that.removeContent(category, id); + that.categories[category].remove(c); + + that.decreaseLayerCount(); + + if (that.categories[category].length == 0) { + delete that.categories[category]; + } + } + }); + + setTimeout( function() { that.resize(); }, 300); + }); + }, + + removeContent: function(category, id, hide) { + if (this.categories[category].length == 1) { + this.$el.find('ul.' + category).fadeOut(250, function() { + $(this).remove(); + }); + + if (!hide && _.size(this.categories) == 1) { + this.hide(); + } + } else { + this.$el.find('li#' + id).fadeOut(250, function() { + $(this).remove(); + }); + } + }, + + addContent: function(item) { + var that = this; + + var slug = item.get('slug'); + + if (this.categories[item.attributes.category].length == 1) { + var template_name; + + if (slug == 'imazon') { + template_name = 'legend-group-double-template'; + } else if (slug == 'umd_tree_loss_gain') { + template_name = 'legend-loss-template'; + } else { + template_name = 'legend-group-template'; + } + + var template = new cdb.core.Template({ + template: $('#' + template_name).html(), + type: 'mustache' + }); + + if (slug == 'forest2000') { + item.attributes.legend = 'Percent Tree Cover'; + item.attributes.legend_class = 'tree'; + + item.attributes.colors = [{ color: '#9DD89D', title: '25-50%' }, { color: '#4EC54E', title: '50-75%' }, { color: '#00B300', title: '75-100%' } ]; + } else if (slug == 'pantropical') { + item.attributes.legend = "Total Biomass (Mg C Ha-1)"; + item.attributes.legend_class = ''; + item.attributes.min = '75'; + item.attributes.max = '383'; + item.attributes.wide = [{ 'color_min': '#FFFFD4', 'color_max': '#dd8653'} ]; + } else { + item.attributes.legend = false; + } + + var $item = template.render(item.attributes); + + if (this.model.get('scrollbar')) { + this.$content.find('.jspPane').prepend( $item ); + } else { + this.$content.append( $item ); + } + + this.$content.find('li.' + item.attributes.category).fadeIn(250); + + if (config.BASELAYER === 'loss') { + this.$content.find('li#' + item.attributes.cat_id + '.loss').fadeIn(250); + } + + if (_.include(config.MAPOPTIONS.layers, 596)) { + this.$content.find('li#' + item.attributes.cat_id + '.forestgain').fadeIn(250); + } + } else { + var template_name; + + template_name = '#legend-item-template'; + + var template = new cdb.core.Template({ + template: $(template_name).html(), + type: 'mustache' + }); + + if (slug == 'forest2000') { + item.attributes.legend = 'Percent Tree Cover'; + item.attributes.legend_class = 'tree'; + item.attributes.colors = [{ color: '#9DD89D', title: '25-50%' }, { color: '#4EC54E', title: '50-75%' }, { color: '#00B300', title: '75-100%' } ]; + } else if (slug == 'pantropical') { + item.attributes.legend = "Total Biomass Carbon (Mg C Ha-1)"; + item.attributes.legend_class = ''; + item.attributes.min = '75'; + item.attributes.max = '383'; + item.attributes.wide = [{ 'color_min': '#FFFFD4', 'color_max': '#dd8653'} ]; + } else { + item.attributes.legend = false; + } + + var $item = template.render(item.attributes); + + this.$content.find('ul.' + item.attributes.category).append( $item ); + this.$content.find('li#' + item.attributes.cat_id).fadeIn(250); + } + + setTimeout(function() { + that.$el.find('.checkbox').on('click', function(e) { + e.preventDefault(); + + item.attributes.subevent && item.attributes.subevent(); + }); + + that.resize(); + }, 300); + }, + + resize: function() { + var that = this; + + var height = this.$el.find('.jspPane').height(); + + this.$content.animate({ height: height }, { duration: 100, complete: function() { + that.addScroll(); + }}); + }, + + toggleOpen: function() { + var that = this; + + if (this.model.get('closed')) { + that.model.set('contentHeight', that.$content.height()); + + if (that.model.get('layerCount') != 1) { + that.$layer_count.html( that.model.get('layerCount') + ' layers'); + } else { + that.$layer_count.html( that.model.get('layerCount') + ' layer'); + } + + that.$content.animate({ opacity: 0, height: 0 }, 250, function() { + that.$layer_count.fadeIn(250); + that.$shadow.fadeOut(250); + }); + + that.$el.addClass('closed'); + } else { + that.$layer_count.fadeOut(250, function() { + that.$content.animate({ opacity: 1, height: that.model.get('contentHeight') }, 250); + that.$shadow.fadeIn(250); + }); + + that.$el.removeClass('closed'); + } + }, + + render: function() { + var that = this; + this.$el.append(this.template.render( this.model.toJSON() )); + this.$content = this.$el.find('.content'); + this.$layer_count = this.$el.find('.layer_count'); + this.$shadow = this.$el.find('.shadow'); + + if ($('body').hasClass('embed')){ + this.$el.hide(); + setTimeout(function(){ + that.model.set('closed', true); + that.toggleOpen; + that.$el.css('left','60px').delay(300).fadeIn(); + },1000) + } + + return this.$el; + } +}); diff --git a/app/assets/javascripts/map/views/search.html b/app/assets/javascripts/map/views/search.html new file mode 100644 index 0000000000..a9241a59d9 --- /dev/null +++ b/app/assets/javascripts/map/views/search.html @@ -0,0 +1,5 @@ + \ No newline at end of file diff --git a/app/assets/javascripts/map/views/share.html b/app/assets/javascripts/map/views/share.html new file mode 100644 index 0000000000..1e6c51e98d --- /dev/null +++ b/app/assets/javascripts/map/views/share.html @@ -0,0 +1,27 @@ + + + \ No newline at end of file From f04f8f3330ff72d7454f022f4dd1b8dac03c025e Mon Sep 17 00:00:00 2001 From: Aaron Date: Wed, 18 Jun 2014 12:10:57 -0700 Subject: [PATCH 097/823] WIP: Add more analysis tests --- app/assets/javascripts/map/analysis.js | 11 ++-- app/assets/javascripts/map/nsa.js | 12 ++--- jstest/helpers/api_responses.js | 6 ++- jstest/spec/analysis_spec.js | 69 ++++++++++++++++++++++---- 4 files changed, 75 insertions(+), 23 deletions(-) diff --git a/app/assets/javascripts/map/analysis.js b/app/assets/javascripts/map/analysis.js index c355fc3d25..e0206aba0e 100644 --- a/app/assets/javascripts/map/analysis.js +++ b/app/assets/javascripts/map/analysis.js @@ -31,9 +31,9 @@ define([ * @return {string} URI template for API */ get_uritemplate: function(config) { - if (_.has(config, 'iso')) { + if (_.has(config, 'iso') && !_.has(config, 'id1')) { return this.apis.national; - } else if (_.has(config, 'id1')) { + } else if (_.has(config, 'iso') && _.has(config, 'id1')) { return this.apis.subnational; } else if (_.has(config, 'use')) { return this.apis.use; @@ -81,7 +81,8 @@ define([ * geojson - GeoJSON Polygon or Multipolygon * iso - 3 letter country ISO code (e.g., BRA) * id1 - GADM subational id (e.g., 3445) - * use - Concession name and polygon cartodb_id (e.g., logging,1) + * use - Concession name (e.g., logging, mining, oilpalm, fiber) + * useid - Concession polygon cartodb_id (e.g., 2) * wdpa - WDPA polygon cartodb_id (e.g., 800) */ execute: function(config) { @@ -93,8 +94,8 @@ define([ function(response) { mps.publish('analysis/get-results', [response]); }, - function(status, msg) { - mps.publish('analysis/get-results-error', [status, msg]); + function(responseText, status, error) { + mps.publish('analysis/get-results-error', [responseText, status, error]); }); } }); diff --git a/app/assets/javascripts/map/nsa.js b/app/assets/javascripts/map/nsa.js index e9515e1516..1a8ae84891 100644 --- a/app/assets/javascripts/map/nsa.js +++ b/app/assets/javascripts/map/nsa.js @@ -12,7 +12,7 @@ define([ * data: Object with parameters. * callback: Object with a success and error function. */ - spy: function(url, data, success, error) { + spy: function(url, data, successCb, errorCb) { var jqxhr = null; var key = null; var val = null; @@ -22,13 +22,13 @@ define([ type: "POST", data: JSON.stringify(data), success: function(response) { - if (success) { - success(response); + if (successCb) { + successCb(response); } }, - error: function(status, msg) { - if (error) { - error(status, msg); + error: function(jqxhr, status, error) { + if (errorCb) { + errorCb(jqxhr.responseText, status, error); } }, contentType: 'application/json', diff --git a/jstest/helpers/api_responses.js b/jstest/helpers/api_responses.js index dc8fc4f1da..6cf1c48195 100644 --- a/jstest/helpers/api_responses.js +++ b/jstest/helpers/api_responses.js @@ -4,7 +4,11 @@ var ApiResponse = { success: { status: 200, responseText: '{"coverage": "Humid tropical forest biome", "description": "Alerts where forest disturbances have likely occurred.", "id": "forma-alerts", "iso": "bra", "name": "FORMA", "resolution": "500 x 500 meters", "source": "MODIS", "timescale": "January 2006 to present", "units": "Alerts", "updates": "16 day", "value": 1088866}' - } + }, + notfound: { + status: 404, + responseText: '404 Not Found

      404 Not Found

      The resource could not be found.

      ' + } }, wdpa: { success: { diff --git a/jstest/spec/analysis_spec.js b/jstest/spec/analysis_spec.js index 3d43e27ca8..be7f3738fd 100644 --- a/jstest/spec/analysis_spec.js +++ b/jstest/spec/analysis_spec.js @@ -19,7 +19,7 @@ define([ expect(analysis).not.toBe(null); }); - it("correctly classifies apis", function() { + it("returns correct URI templates based on config", function() { var template = analysis.get_uritemplate({iso: 'bra'}); var url = analysis.get_url({layerName: 'forma-alerts', iso: 'bra'}); expect(template).toBe(apis.national); @@ -27,35 +27,46 @@ define([ }); }); - describe("Run analysis via mps.publish(analysis/get)", function() { - var requeat = null; + describe("Analyze forest change layers", function() { + var request = null; + var originalTimeout; beforeEach(function() { jasmine.Ajax.install(); + originalTimeout = jasmine.DEFAULT_TIMEOUT_INTERVAL; + jasmine.DEFAULT_TIMEOUT_INTERVAL = 10000; + }); - mps.publish('analysis/get', [{layerName: 'forma-alerts', iso: 'bra'}]); - - request = jasmine.Ajax.requests.mostRecent(); - expect(request.url).toBe('http://beta.gfw-apis.appspot.com/forest-change/forma-alerts/admin/bra'); - expect(request.method).toBe('POST'); - expect(request.data()).toEqual({}); + afterEach(function() { + jasmine.DEFAULT_TIMEOUT_INTERVAL = originalTimeout; }); - describe("Check mps.publish(analysis/get-results)", function() { + describe("FORMA Alerts by country - success", function() { var callback = null; + var url = 'http://beta.gfw-apis.appspot.com/forest-change/forma-alerts/admin/bra?period=2001%2C2008'; beforeEach(function(done) { + mps.publish( + 'analysis/get', + [{layerName: 'forma-alerts', iso: 'bra', period: '2001,2008'}]); + + request = jasmine.Ajax.requests.mostRecent(); + expect(request.url).toBe(url); + expect(request.method).toBe('POST'); + expect(request.data()).toEqual({}); + callback = { spy: function(response) { done(); } }; + spyOn(callback, 'spy').and.callThrough(); mps.subscribe('analysis/get-results', callback.spy); request.response(ApiResponse.forma_alerts.iso.success); }); - it("fires subscriber callback with correct API response", function() { + it("The analysis/get-results callback called with correct API response", function() { var response = null; var expected = JSON.parse(ApiResponse.forma_alerts.iso.success.responseText); @@ -64,5 +75,41 @@ define([ expect(response).toEqual(expected); }); }); + + + describe("FORMA Alerts by country - failure", function() { + var callback = null; + var url = 'http://beta.gfw-apis.appspot.com/forest-change/forma-alerts-foo/admin/bra?period=2001%2C2008'; + + beforeEach(function(done) { + mps.publish( + 'analysis/get', + [{layerName: 'forma-alerts-foo', iso: 'bra', period: '2001,2008'}]); + + request = jasmine.Ajax.requests.mostRecent(); + expect(request.url).toBe(url); + expect(request.method).toBe('POST'); + expect(request.data()).toEqual({}); + + callback = { + spy: function(responseText, status, error) { + done(); + } + }; + + spyOn(callback, 'spy').and.callThrough(); + mps.subscribe('analysis/get-results-error', callback.spy); + request.response(ApiResponse.forma_alerts.iso.notfound); + }); + + it("The analysis/get-results callback called with correct API response", function() { + var response = null; + var expected = ApiResponse.forma_alerts.iso.notfound.responseText; + + expect(callback.spy).toHaveBeenCalled(); + response = callback.spy.calls.mostRecent().args[0]; + expect(response).toEqual(expected); + }); + }); }); }); \ No newline at end of file From 887c098b068b7cdf155e2e3a237b3ef5783a9b09 Mon Sep 17 00:00:00 2001 From: pedro Date: Wed, 18 Jun 2014 21:40:26 +0200 Subject: [PATCH 098/823] imazon layer --- app/assets/javascripts/map/gmap.js | 3 +- app/assets/javascripts/map/mediator.js | 8 +- app/assets/javascripts/map/router.js | 5 +- .../map/views/layers/core/cartodbLayer.js | 118 ++++++++++-------- .../javascripts/map/views/layers/imazon.js | 37 ++++-- .../javascripts/map/views/layers/loss.js | 7 +- app/assets/javascripts/map/views/timeline.js | 13 +- config/requirejs.yml | 9 +- ...{cartodb-gmapsv3.js => cartodb.gmapsv3.js} | 0 9 files changed, 119 insertions(+), 81 deletions(-) rename vendor/assets/javascripts/{cartodb-gmapsv3.js => cartodb.gmapsv3.js} (100%) diff --git a/app/assets/javascripts/map/gmap.js b/app/assets/javascripts/map/gmap.js index 85fb90fbc8..e24e960c8d 100644 --- a/app/assets/javascripts/map/gmap.js +++ b/app/assets/javascripts/map/gmap.js @@ -33,9 +33,10 @@ define([ // After each lib is loaded, get the cartodb lib. var done = _.after(libs.length, function () { - require(['cartodb'], cb); + require(['cartodb', 'cartodblayer'], cb); }); + // Load the jsapi and then grab each lib. require(['https://www.google.com/jsapi?callback=?' + '&key=AIzaSyDJdVhfQhecwp0ngAGzN9zwqak8FaEkSTA'], function () { diff --git a/app/assets/javascripts/map/mediator.js b/app/assets/javascripts/map/mediator.js index b6c246b770..11d407170e 100644 --- a/app/assets/javascripts/map/mediator.js +++ b/app/assets/javascripts/map/mediator.js @@ -15,8 +15,9 @@ define([ 'collections/layers', 'views/map', 'views/layers/loss', - 'views/layers/forest' -], function (_, Backbone, mps, Class, presenter, layersCollection, map, LossLayer, ForestLayer) { + 'views/layers/forest', + 'views/layers/imazon' +], function (_, Backbone, mps, Class, presenter, layersCollection, map, LossLayer, ForestLayer, ImazonLayer) { var Mediator = Class.extend({ init: function() { @@ -37,7 +38,8 @@ define([ baselayersOpts: { views: { loss: LossLayer, - gain: LossLayer + gain: LossLayer, + imazon: ImazonLayer }, allowedCombined: [ ['loss', 'gain'] diff --git a/app/assets/javascripts/map/router.js b/app/assets/javascripts/map/router.js index ee4d7ecfc5..19c6bb2070 100644 --- a/app/assets/javascripts/map/router.js +++ b/app/assets/javascripts/map/router.js @@ -20,8 +20,7 @@ define([ routes: { 'map': 'map', - 'map/:zoom/:lat/:lng/:iso/:maptype/:baselayers': 'map', - 'map/:zoom/:lat/:lng/:iso/:maptype/:baselayers/:sublayers': 'map', + 'map/:zoom/:lat/:lng/:iso/:maptype/:baselayers(/:sublayers)(/)': 'map', }, initialize: function() { @@ -63,8 +62,8 @@ define([ hh = $('.header').height(), $map = $('#map'); - $map.height(dh - hh); $('html, body').scrollTop($map.offset().top - 67); + $map.height(dh - hh); } }); diff --git a/app/assets/javascripts/map/views/layers/core/cartodbLayer.js b/app/assets/javascripts/map/views/layers/core/cartodbLayer.js index 565ce363d0..c0b4c06a89 100644 --- a/app/assets/javascripts/map/views/layers/core/cartodbLayer.js +++ b/app/assets/javascripts/map/views/layers/core/cartodbLayer.js @@ -1,67 +1,75 @@ -App.Views.CartodbLayer = cdb.core.View.extend({ +/** + * The Cartodb map layer module. + * + * @return CartodbLayer class (extends Backbone.View). + */ +define([ + 'backbone', + 'mps', + 'views/map', + 'presenter', + 'wax' +], function(Backbone, mps, map, presenter, wax) { - initialize: function() { - this.map = app.views.map.map; - this.layer = {}; - this.layerOrder = this.layerOrder || 1; - this.rendered = false; - }, + var CartodbLayer = Backbone.View.extend({ + initialize: function() { + this.layer = {}; + this.layerOrder = this.layerOrder || 1; + this.rendered = false; + }, - render: function() { - if (this.rendered) return; + render: function() { + if (this.rendered) return; - this.layer = new CartoDBLayer({ - map: this.map, - user_name: '', - tiler_domain: this.url, - sql_domain: this.url, - extra_params: { v: this.global_version}, - tiler_path: '/tiles/', - tiler_suffix: '.png', - tiler_grid: '.grid.json', - table_name: this.table, - query: this.getQuery(), - layer_order: this.layerOrder, - opacity: 1, - interactivity: "cartodb_id", - debug: false, - auto_bound: false - }); + this.layer = new CartoDBLayer({ + map: map.map, + user_name: '', + tiler_domain: this.url, + sql_domain: this.url, + extra_params: { v: this.global_version}, + tiler_path: '/tiles/', + tiler_suffix: '.png', + tiler_grid: '.grid.json', + table_name: this.table, + query: this.getQuery(), + layer_order: this.layerOrder, + opacity: 1, + interactivity: "cartodb_id", + debug: false, + auto_bound: false + }); - this.rendered = true; - }, + this.rendered = true; + }, - updateTiles: function() { - this.layer.setQuery(this.getQuery()); - }, + updateTiles: function() { + this.layer.setQuery(this.getQuery()); + }, - getQuery: function() { - var timelineDate = app.presenter.get('timelineDate') || this.timeline.opts.dateRange; + getQuery: function() { + var timelineDate = presenter.get('timelineDate') || this.timeline.opts.dateRange; - var sql = "SELECT * FROM " + - this.table + - " WHERE date between '" + - timelineDate[0].year() + - "-" + - timelineDate[0].month() + - "-1' AND '" + - timelineDate[1].year() + - "-" + - timelineDate[1].month() + - "-1'"; + var sql = "SELECT * FROM " + + this.table + + " WHERE date between '" + + timelineDate[0].year() + + "-" + + timelineDate[0].month() + + "-1' AND '" + + timelineDate[1].year() + + "-" + + timelineDate[1].month() + + "-1'"; - return sql; - }, + return sql; + }, - removeLayer: function() { - var overlays_length = this.map.overlayMapTypes.getLength(); - if (overlays_length > 0) { - for (var i = 0; i< overlays_length; i++) { - var layer = this.map.overlayMapTypes.getAt(i); - if (layer && layer.name == this.table) this.map.overlayMapTypes.removeAt(i); - this.rendered = false; - } + removeLayer: function() { + mps.publish('map/remove-layer', [this.name]); + this.rendered = false; } - } + }); + + return CartodbLayer; }); \ No newline at end of file diff --git a/app/assets/javascripts/map/views/layers/imazon.js b/app/assets/javascripts/map/views/layers/imazon.js index ead2171516..74b91fc951 100644 --- a/app/assets/javascripts/map/views/layers/imazon.js +++ b/app/assets/javascripts/map/views/layers/imazon.js @@ -1,13 +1,32 @@ -App.Views.ImazonLayer = App.Views.CartodbLayer.extend({ +/** + * The Imazon layer module for use on canvas. + * + * @return ImazonLayer class (extends CartodbLayer) + */ +define([ + 'backbone', + 'views/layers/core/cartodbLayer', + 'views/timeline', + 'moment' +], function(Backbone, CartodbLayer, Timeline, moment) { - initialize: function() { - this.layerName = "imazon"; - this.url = 'dyynnn89u7nkm.cloudfront.net'; - this.table = 'imazon_clean2'; - this.global_version = 6; + var ImazonLayer = CartodbLayer.extend({ - App.Views.ImazonLayer.__super__.initialize.apply(this); - //[moment([2007, 1, 1]), moment([2011, 8, 1])] - } + initialize: function() { + this.layerName = "imazon"; + this.url = 'dyynnn89u7nkm.cloudfront.net'; + this.table = 'imazon_clean2'; + this.global_version = 6; + ImazonLayer.__super__.initialize.apply(this); + + this.timeline = new Timeline({ + dateRange: [moment([2007, 1, 1]), moment([2011, 8, 1])], + layerName: 'imazon' + }); + } + + }); + + return ImazonLayer; }); \ No newline at end of file diff --git a/app/assets/javascripts/map/views/layers/loss.js b/app/assets/javascripts/map/views/layers/loss.js index 0cc18bdbbf..7536cea55e 100644 --- a/app/assets/javascripts/map/views/layers/loss.js +++ b/app/assets/javascripts/map/views/layers/loss.js @@ -7,9 +7,10 @@ define([ 'backbone', 'mps', 'presenter', + 'moment', 'views/layers/core/canvasLayer', 'views/timeline' -], function(Backbone, mps, presenter, CanvasLayer, Timeline) { +], function(Backbone, mps, presenter, moment, CanvasLayer, Timeline) { var LossLayer = CanvasLayer.extend({ @@ -20,7 +21,7 @@ define([ LossLayer.__super__.initialize.apply(this); this.timeline = new Timeline({ - dateRange: [2001, 2013], + dateRange: [moment([2001]), moment([2013])], layerName: 'loss' }); @@ -31,6 +32,8 @@ define([ z = presenter.get('zoom'), timelineDate = presenter.get('timelineDate') || this.timeline.opts.dateRange; + timelineDate = [timelineDate[0].year(), timelineDate[1].year()]; + for(var i = 0; i < w; ++i) { for(var j = 0; j < h; ++j) { var pixelPos = (j * w + i) * components, diff --git a/app/assets/javascripts/map/views/timeline.js b/app/assets/javascripts/map/views/timeline.js index 571209900c..cf2dc1056f 100644 --- a/app/assets/javascripts/map/views/timeline.js +++ b/app/assets/javascripts/map/views/timeline.js @@ -21,7 +21,7 @@ define([ _.bindAll(this, 'onAnimate', 'onBrush', 'onBrushEnd'); this.opts = _.extend({ - dateRange: [2001, moment().year()], + dateRange: [moment([2001]), moment()], layerName: '', xAxis: { months: { @@ -62,7 +62,7 @@ define([ height = 40 - margin.bottom - margin.top; this.xscale = d3.scale.linear() - .domain(this.opts.dateRange) + .domain([this.opts.dateRange[0].year(), this.opts.dateRange[1].year()]) .range([0, width]) .clamp(true); @@ -89,7 +89,7 @@ define([ .call(d3.svg.axis() .scale(this.xscale) .orient("bottom") - .ticks(this.opts.dateRange[1] - this.opts.dateRange[0]) + .ticks(this.opts.dateRange[1].year() - this.opts.dateRange[0].year()) .tickFormat(function(d) { return String(d); }) .tickSize(0) .tickPadding(12)) @@ -113,7 +113,7 @@ define([ this.handlers.right = this.handlers.left .select(function() { return this.parentNode.appendChild(this.cloneNode(true)); }) - .attr('cx', this.xscale(this.opts.dateRange[1])) + .attr('cx', this.xscale(this.opts.dateRange[1].year())) .style('fill', 'green'); this.slider.selectAll(".extent,.resize") @@ -145,7 +145,6 @@ define([ onAnimate: function() { var value = this.hiddenBrush.extent()[0]; - var timelineDate = presenter.get('timelineDate'); if (!this.playing) return; @@ -220,7 +219,7 @@ define([ .ease('line') .attr("cx", this.xscale(Math.round(value))); this.updateSelectedDomain(Math.round(value), 'right'); - if (brushend) this.updateTimelineDate([timelineDate[0], Math.round(value)]); + if (brushend) this.updateTimelineDate([timelineDate[0], moment([Math.round(value)])]); } else { this.handlers.left .transition() @@ -230,7 +229,7 @@ define([ this.updateSelectedDomain(Math.round(value), 'left'); if (brushend) { - this.updateTimelineDate([Math.round(value), timelineDate[1]]); + this.updateTimelineDate([moment([Math.round(value)]), timelineDate[1]]); } } }, diff --git a/config/requirejs.yml b/config/requirejs.yml index 947bdfefdc..d2fac893c4 100644 --- a/config/requirejs.yml +++ b/config/requirejs.yml @@ -10,7 +10,9 @@ paths: gmap: "map/gmap" d3: "d3" backbone_cartodb: "backbone.cartodb" + cartodblayer: "cartodb.gmapsv3" cartodb: "cartodb" + wax: "wax.g.min" store: "store" text: "text" Class: "class" @@ -27,7 +29,7 @@ shim: underscore: exports: "_" backbone: - deps: + deps: - "jquery" - "underscore" exports: "Backbone" @@ -36,6 +38,10 @@ shim: - "underscore" - "backbone" exports: "backbone_cartodb" + cartodblayer: + deps: + - "gmap" + exports: "CartoDBLayer" backbonequeryparams: deps: - "backbone" @@ -47,6 +53,7 @@ shim: deps: - "mps" - "Class" + - "gmap" exports: "app" user: deps: diff --git a/vendor/assets/javascripts/cartodb-gmapsv3.js b/vendor/assets/javascripts/cartodb.gmapsv3.js similarity index 100% rename from vendor/assets/javascripts/cartodb-gmapsv3.js rename to vendor/assets/javascripts/cartodb.gmapsv3.js From e2210325b48b9e87bb6e3842c50d46fc22f0cb97 Mon Sep 17 00:00:00 2001 From: pedro Date: Wed, 18 Jun 2014 21:47:52 +0200 Subject: [PATCH 099/823] timeline imazon --- .../javascripts/map/views/layers/core/cartodbLayer.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/app/assets/javascripts/map/views/layers/core/cartodbLayer.js b/app/assets/javascripts/map/views/layers/core/cartodbLayer.js index c0b4c06a89..8dc915c880 100644 --- a/app/assets/javascripts/map/views/layers/core/cartodbLayer.js +++ b/app/assets/javascripts/map/views/layers/core/cartodbLayer.js @@ -43,6 +43,7 @@ define([ }, updateTiles: function() { + console.log(this.getQuery()) this.layer.setQuery(this.getQuery()); }, @@ -54,12 +55,12 @@ define([ " WHERE date between '" + timelineDate[0].year() + "-" + - timelineDate[0].month() + - "-1' AND '" + + //timelineDate[0].month() + + "1-1' AND '" + timelineDate[1].year() + "-" + - timelineDate[1].month() + - "-1'"; + //timelineDate[1].month() + + "1-1'"; return sql; }, From 6ffb822dd5dda93eaae5c7a99e67cff3e771f421 Mon Sep 17 00:00:00 2001 From: pedro Date: Wed, 18 Jun 2014 21:48:21 +0200 Subject: [PATCH 100/823] fix --- app/assets/javascripts/map/views/layers/core/cartodbLayer.js | 1 - 1 file changed, 1 deletion(-) diff --git a/app/assets/javascripts/map/views/layers/core/cartodbLayer.js b/app/assets/javascripts/map/views/layers/core/cartodbLayer.js index 8dc915c880..edea4ddf00 100644 --- a/app/assets/javascripts/map/views/layers/core/cartodbLayer.js +++ b/app/assets/javascripts/map/views/layers/core/cartodbLayer.js @@ -43,7 +43,6 @@ define([ }, updateTiles: function() { - console.log(this.getQuery()) this.layer.setQuery(this.getQuery()); }, From acd5d0110bfe9bc9a720409879366ecf45d95401 Mon Sep 17 00:00:00 2001 From: pedro Date: Thu, 19 Jun 2014 15:07:14 +0200 Subject: [PATCH 101/823] templates --- app/assets/javascripts/map/mediator.js | 3 ++- .../{javascripts/map/views => templates/map}/analysis.html | 0 .../{javascripts/map/views => templates/map}/circle.html | 0 .../{javascripts/map/views => templates/map}/filter.html | 0 .../{javascripts/map/views => templates/map}/legend.html | 0 .../{javascripts/map/views => templates/map}/search.html | 0 app/assets/{javascripts/map/views => templates/map}/share.html | 0 config/application.rb | 2 +- 8 files changed, 3 insertions(+), 2 deletions(-) rename app/assets/{javascripts/map/views => templates/map}/analysis.html (100%) rename app/assets/{javascripts/map/views => templates/map}/circle.html (100%) rename app/assets/{javascripts/map/views => templates/map}/filter.html (100%) rename app/assets/{javascripts/map/views => templates/map}/legend.html (100%) rename app/assets/{javascripts/map/views => templates/map}/search.html (100%) rename app/assets/{javascripts/map/views => templates/map}/share.html (100%) diff --git a/app/assets/javascripts/map/mediator.js b/app/assets/javascripts/map/mediator.js index b7a8cd4c55..0a2f5dce27 100644 --- a/app/assets/javascripts/map/mediator.js +++ b/app/assets/javascripts/map/mediator.js @@ -17,10 +17,11 @@ define([ 'views/layers/loss', 'views/layers/forest', 'views/layers/imazon' -], function (_, Backbone, mps, Class, presenter, layersCollection, map, LossLayer, ForestLayer, ImazonLayer) { +], function (_, Backbone, mps, Class, presenter, layersCollection, map, LossLayer, ForestLayer, ImazonLayer, analysisTpl) { var Mediator = Class.extend({ init: function() { + console.log(analysisTpl); // Listen to presenter events presenter.on('change:zoom', this.updateZoom, this); diff --git a/app/assets/javascripts/map/views/analysis.html b/app/assets/templates/map/analysis.html similarity index 100% rename from app/assets/javascripts/map/views/analysis.html rename to app/assets/templates/map/analysis.html diff --git a/app/assets/javascripts/map/views/circle.html b/app/assets/templates/map/circle.html similarity index 100% rename from app/assets/javascripts/map/views/circle.html rename to app/assets/templates/map/circle.html diff --git a/app/assets/javascripts/map/views/filter.html b/app/assets/templates/map/filter.html similarity index 100% rename from app/assets/javascripts/map/views/filter.html rename to app/assets/templates/map/filter.html diff --git a/app/assets/javascripts/map/views/legend.html b/app/assets/templates/map/legend.html similarity index 100% rename from app/assets/javascripts/map/views/legend.html rename to app/assets/templates/map/legend.html diff --git a/app/assets/javascripts/map/views/search.html b/app/assets/templates/map/search.html similarity index 100% rename from app/assets/javascripts/map/views/search.html rename to app/assets/templates/map/search.html diff --git a/app/assets/javascripts/map/views/share.html b/app/assets/templates/map/share.html similarity index 100% rename from app/assets/javascripts/map/views/share.html rename to app/assets/templates/map/share.html diff --git a/config/application.rb b/config/application.rb index d9cbe2ee5e..b43f2b31c9 100644 --- a/config/application.rb +++ b/config/application.rb @@ -28,7 +28,7 @@ class Application < Rails::Application # config.i18n.default_locale = :de I18n.enforce_available_locales = false - + config.assets.paths << File.join(Rails.root, 'app', 'assets', 'templates') config.autoload_paths += %W(#{config.root}/lib) end end From 40425640381b628c086888a498e6a9103486baed Mon Sep 17 00:00:00 2001 From: pedro Date: Thu, 19 Jun 2014 15:07:42 +0200 Subject: [PATCH 102/823] hotfix --- app/assets/javascripts/map/mediator.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/assets/javascripts/map/mediator.js b/app/assets/javascripts/map/mediator.js index 0a2f5dce27..b7a8cd4c55 100644 --- a/app/assets/javascripts/map/mediator.js +++ b/app/assets/javascripts/map/mediator.js @@ -17,11 +17,10 @@ define([ 'views/layers/loss', 'views/layers/forest', 'views/layers/imazon' -], function (_, Backbone, mps, Class, presenter, layersCollection, map, LossLayer, ForestLayer, ImazonLayer, analysisTpl) { +], function (_, Backbone, mps, Class, presenter, layersCollection, map, LossLayer, ForestLayer, ImazonLayer) { var Mediator = Class.extend({ init: function() { - console.log(analysisTpl); // Listen to presenter events presenter.on('change:zoom', this.updateZoom, this); From 806a59d1e59919ee72a3b5ada762a2ab9ec17c25 Mon Sep 17 00:00:00 2001 From: pedro Date: Thu, 19 Jun 2014 16:47:16 +0200 Subject: [PATCH 103/823] gain layer --- app/assets/javascripts/map/mediator.js | 5 +- .../map/views/layers/core/canvasLayer.js | 3 +- .../map/views/layers/core/imageLayer.js | 89 +++++++++++++++++++ .../javascripts/map/views/layers/gain.js | 27 ++++++ app/assets/javascripts/map/views/timeline.js | 3 +- 5 files changed, 121 insertions(+), 6 deletions(-) create mode 100644 app/assets/javascripts/map/views/layers/core/imageLayer.js create mode 100644 app/assets/javascripts/map/views/layers/gain.js diff --git a/app/assets/javascripts/map/mediator.js b/app/assets/javascripts/map/mediator.js index b7a8cd4c55..6071c2d081 100644 --- a/app/assets/javascripts/map/mediator.js +++ b/app/assets/javascripts/map/mediator.js @@ -15,9 +15,10 @@ define([ 'collections/layers', 'views/map', 'views/layers/loss', + 'views/layers/gain', 'views/layers/forest', 'views/layers/imazon' -], function (_, Backbone, mps, Class, presenter, layersCollection, map, LossLayer, ForestLayer, ImazonLayer) { +], function (_, Backbone, mps, Class, presenter, layersCollection, map, LossLayer, GainLayer, ForestLayer, ImazonLayer) { var Mediator = Class.extend({ init: function() { @@ -38,7 +39,7 @@ define([ baselayersOpts: { views: { loss: LossLayer, - gain: LossLayer, + gain: GainLayer, imazon: ImazonLayer }, allowedCombined: [ diff --git a/app/assets/javascripts/map/views/layers/core/canvasLayer.js b/app/assets/javascripts/map/views/layers/core/canvasLayer.js index d866a4dede..f302e2e542 100644 --- a/app/assets/javascripts/map/views/layers/core/canvasLayer.js +++ b/app/assets/javascripts/map/views/layers/core/canvasLayer.js @@ -16,12 +16,11 @@ define([ this.rendered = false; }, - // TODO: MPS so map adds layer render: function() { if (!this.rendered) { mps.publish('map/add-layer', [this]); + this.rendered = true; } - this.rendered = true; }, getTile: function(coord, zoom, ownerDocument) { diff --git a/app/assets/javascripts/map/views/layers/core/imageLayer.js b/app/assets/javascripts/map/views/layers/core/imageLayer.js new file mode 100644 index 0000000000..345d0a5e49 --- /dev/null +++ b/app/assets/javascripts/map/views/layers/core/imageLayer.js @@ -0,0 +1,89 @@ +/** + * The Image map layer module. + * + * @return ImageLayer class (extends Backbone.View). + */ +define([ + 'backbone', + 'mps' +], function(Backbone, mps) { + + var ImageLayer = Backbone.View.extend({ + + initialize: function () { + this.tileSize = new google.maps.Size(256, 256); + this.tiles = {}; + this.rendered = false; + }, + + render: function() { + if (!this.rendered) { + mps.publish('map/add-layer', [this]); + this.rendered = true; + } + }, + + getTile: function(coord, zoom, ownerDocument) { + var x = coord.x; + var y = coord.y; + var z = zoom; + + var zsteps = zoom - 12; + + if (zoom > 12) { + x = Math.floor(coord.x/(Math.pow(2, zoom - 12))); + y = Math.floor(coord.y/(Math.pow(2, zoom - 12))); + z = 12; + } else { + y = (y > Math.pow(2,z) ? y % Math.pow(2,z) : y); + + if (x >= Math.pow(2,z)) { + x = x % Math.pow(2,z); + } else if (x < 0) { + x = Math.pow(2,z) - Math.abs(x); + } + } + + var url = this.url.replace('%z', z).replace('%x', x).replace('%y', y); + + var image = new Image(); + image.src = url; + image.className += this.name; + + if (zsteps <= 0) return image; + + image.width = 256 * Math.pow(2, zsteps); + image.height = 256 * Math.pow(2, zsteps); + + if (zsteps > 0) { + var srcX = 256 * (coord.x % Math.pow(2, zsteps)); + var srcY = 256 * (coord.y % Math.pow(2, zsteps)); + + image.style.position = 'absolute'; + image.style.top = -srcY + 'px'; + image.style.left = -srcX + 'px'; + } + + var div = ownerDocument.createElement('div'); + div.appendChild(image); + div.style.width = this.tileSize.width + 'px'; + div.style.height = this.tileSize.height + 'px'; + div.style.position = 'relative'; + div.style.overflow = 'hidden'; + div.className += this.name; + + return div; + }, + + updateTiles: function() { + }, + + removeLayer: function() { + mps.publish('map/remove-layer', [this.name]); + this.rendered = false; + } + + }); + + return ImageLayer; +}); \ No newline at end of file diff --git a/app/assets/javascripts/map/views/layers/gain.js b/app/assets/javascripts/map/views/layers/gain.js new file mode 100644 index 0000000000..651738b009 --- /dev/null +++ b/app/assets/javascripts/map/views/layers/gain.js @@ -0,0 +1,27 @@ +/** + * The Imazon layer module for use on canvas. + * + * @return GainLayer class (extends CartodbLayer) + */ +define([ + 'backbone', + 'views/layers/core/imageLayer', + 'views/timeline', + 'moment' +], function(Backbone, ImageLayer, Timeline, moment) { + + var GainLayer = ImageLayer.extend({ + + initialize: function() { + this.dataMaxZoom = 19; + this.name = "gain"; + GainLayer.__super__.initialize.apply(this); + + this.url = 'http://earthengine.google.org/static/hansen_2013/gain_alpha/%z/%x/%y.png'; + } + + }); + + return GainLayer; + +}); \ No newline at end of file diff --git a/app/assets/javascripts/map/views/timeline.js b/app/assets/javascripts/map/views/timeline.js index cf2dc1056f..1c540ddae3 100644 --- a/app/assets/javascripts/map/views/timeline.js +++ b/app/assets/javascripts/map/views/timeline.js @@ -1,8 +1,7 @@ /** * The timeline module. * - * Timeline for all layers configured by setting layer-specific options via - * the setOpts() function. + * Timeline for all layers configured by setting layer-specific options. * * @return singleton instance of Timeline class (extends Backbone.View). */ From 9c5312cd93f45a0727da1488aba1f5fcea65fdb8 Mon Sep 17 00:00:00 2001 From: pedro Date: Thu, 19 Jun 2014 18:38:57 +0200 Subject: [PATCH 104/823] css reorganizing --- app/assets/javascripts/map/presenter.js | 1 - .../javascripts/map/views/layersFilter.js | 378 ++++ app/assets/stylesheets/_base.scss | 929 ++++++++++ .../stylesheets/{map.css.scss => _grid.scss} | 0 app/assets/stylesheets/_layout.scss | 0 app/assets/stylesheets/_mixins.scss | 344 ++++ .../{fonts.scss => _settings.scss} | 76 +- app/assets/stylesheets/application.scss | 946 +--------- app/assets/stylesheets/helpers.scss | 24 - .../{forms.scss => modules/_mod-form.scss} | 4 - app/assets/stylesheets/modules/_mod-map.scss | 1615 +++++++++++++++++ .../stylesheets/{ => modules}/countries.scss | 14 - .../stylesheets/{ => modules}/home.scss | 21 - .../stylesheets/{ => modules}/static.scss | 14 - .../{ => modules}/static_public.css | 0 .../stylesheets/{ => modules}/stories.scss | 13 - app/assets/templates/{map => }/analysis.html | 0 app/assets/templates/{map => }/circle.html | 0 app/assets/templates/{map => }/filter.html | 0 app/assets/templates/{map => }/legend.html | 0 app/assets/templates/{map => }/search.html | 0 app/assets/templates/{map => }/share.html | 0 app/views/layouts/application.html.erb | 1 - app/views/layouts/map.html.erb | 1 - 24 files changed, 3319 insertions(+), 1062 deletions(-) create mode 100644 app/assets/javascripts/map/views/layersFilter.js create mode 100644 app/assets/stylesheets/_base.scss rename app/assets/stylesheets/{map.css.scss => _grid.scss} (100%) create mode 100644 app/assets/stylesheets/_layout.scss create mode 100644 app/assets/stylesheets/_mixins.scss rename app/assets/stylesheets/{fonts.scss => _settings.scss} (88%) delete mode 100644 app/assets/stylesheets/helpers.scss rename app/assets/stylesheets/{forms.scss => modules/_mod-form.scss} (92%) create mode 100644 app/assets/stylesheets/modules/_mod-map.scss rename app/assets/stylesheets/{ => modules}/countries.scss (98%) rename app/assets/stylesheets/{ => modules}/home.scss (98%) rename app/assets/stylesheets/{ => modules}/static.scss (97%) rename app/assets/stylesheets/{ => modules}/static_public.css (100%) rename app/assets/stylesheets/{ => modules}/stories.scss (96%) rename app/assets/templates/{map => }/analysis.html (100%) rename app/assets/templates/{map => }/circle.html (100%) rename app/assets/templates/{map => }/filter.html (100%) rename app/assets/templates/{map => }/legend.html (100%) rename app/assets/templates/{map => }/search.html (100%) rename app/assets/templates/{map => }/share.html (100%) diff --git a/app/assets/javascripts/map/presenter.js b/app/assets/javascripts/map/presenter.js index 1e54819339..3ebef8338a 100644 --- a/app/assets/javascripts/map/presenter.js +++ b/app/assets/javascripts/map/presenter.js @@ -51,7 +51,6 @@ define([ sublayers: this.get('sublayers') }; - var place = { path: _.values(attrs).join('/'), trigger: false diff --git a/app/assets/javascripts/map/views/layersFilter.js b/app/assets/javascripts/map/views/layersFilter.js new file mode 100644 index 0000000000..d91f5fa735 --- /dev/null +++ b/app/assets/javascripts/map/views/layersFilter.js @@ -0,0 +1,378 @@ +var Filter = (function() { + + var pids, + filters = [], + lastClass = null, + categories = [], + $filters = $('.filters'), + $layer = $('#layer'); + + function _remove(id) { + if (_.include(filters, id)) { + filters = _.without(filters, id); + } + + config.MAPOPTIONS.layers = filters; + } + + function _add(id) { + if (!_.include(filters, id)) { + filters.push(id); + } + + config.MAPOPTIONS.layers = filters; + } + + function _toggle(id) { + if (_.include(filters, id)) { + filters = _.without(filters, id); + } else { + filters.push(id); + } + + config.MAPOPTIONS.layers = filters; + } + + function _show(callback) { + if (!$filters.hasClass('hide')) return; + + var count = categories.length; + + $filters.fadeIn(150, function() { + $filters.find('li').each(function(i, el) { + $(el).delay(i * 50).animate({ opacity: 1 }, 150, 'easeInExpo', function() { + $(this).find('a').animate({ top: '-15px'}, 150); + count--; + + if (count <= 0) { + $filters.removeClass('hide'); + + if (callback) callback(); + _calcFiltersPosition(); + } + }); + }); + }); + } + + function _hide(callback) { + _hideLayer(); + + if ($filters.hasClass('hide')) { + callback && callback(); + + return; + } + + var count = categories.length; + + $($filters.find('li a').get().reverse()).each(function(i, el) { + $(el).delay(i * 50).animate({ top: '15px' }, 150, function() { + $(this).parent().animate({ opacity: '0'}, 150, function() { + --count; + + if (count <= 0) { + $filters.fadeOut(150, function() { + $filters.addClass('hide'); + + if (callback) callback(); + }); + } + }); + }); + }); + } + + function _calcFiltersPosition() { + $filters.find('li').each(function(i, el) { + $(el).data('left-pos', $(el).position().left+1); + }); + } + + function _hideLayer() { + $layer.animate({ opacity: 0 }, 70, function() { + $layer.css('left', -10000); + }); + } + + function _closeOpenFilter() { + var c = $layer.attr('class'); + + if (c === undefined) return; + + clearTimeout(pids); + + pids = setTimeout(function() { + _close(c); + }, 100); + } + + function _close(c) { + $layer.animate({ opacity: 0 }, 70, function() { + $layer.css('left', -10000); + $layer.removeClass(c); + }); + + $layer.css('left', '-10000px'); + } + + function _open() { + var $li = $(this), + lw = $layer.width(), + liClass = $li.attr('data-id'), + l = $li.data('left-pos'), + $line = $li.find('.filter-line'), + lineWidth = $line.width(); + + cancelClose(); + + $layer.removeClass(lastClass); + + var name = $li.find('a').text(); + $layer.find('.filter-title').text(name); + + var color = $li.find('a').css('color'); + $layer.find('.filter-title').css({ color: color }); + $layer.find('.filter-line').css({ backgroundColor: color }); + + $layer.find('.last').removeClass('last'); + $layer.find('.filter-links li').hide(); + $layer.find('.filter-links .'+liClass).show(); + $layer.find('.filter-links .'+liClass).last().addClass('last'); + + $('.filter-links .last').closest('li').css({ 'border-bottom': 0 }); + + $layer.addClass(liClass); + lastClass = liClass; + + var left = (l+$li.width() / 2) - (170 / 2), + height = $layer.find('.filter-links').height(); + + $layer.css({ left: left }); + + $layer.animate({ opacity: 1 }, 250); + $('.filter-scroll').css({ height: height }); + } + + function cancelClose() { + clearTimeout(pids); + } + + function _onMouseEnter() { + $layer.animate({ opacity: 1 }, 150); + } + + function _init() { + $(document).on('mouseenter', '.filters li', _open); + $layer.on('mouseleave', _closeOpenFilter); + + $(document).on('click', '.radio', function(e) { + e.preventDefault(); + + $('.radio[data-name="'+$(this).attr('data-name')+'"]').removeClass('checked'); + $(this).addClass('checked'); + }); + + $(document).on('click', '.checkbox', function(e) { + e.preventDefault(); + + $(this).toggleClass('checked'); + + if ($(this).hasClass('checked')) { + var color = $(this).attr('data-color'); + $(this).css('color', color); + $(this).find('i').css('background-color', color); + } else { + $(this).css('color', '#ccc'); + $(this).find('i').css('background-color', '#ccc'); + } + }); + } + + function _check(id) { + $('#layer a[data-id='+id +']').addClass('checked'); + + var color = $('#layer a[data-id='+id +']').attr('data-color'); + $('#layer a[data-id='+id +']').css('color', color ); + $('#layer a[data-id='+id +']').find('i').css('background-color', color ); + + filters.push(id); + } + + function _resize() { + var height = $('.filter-links').height(); + + $('.filter-scroll').animate({ height: height }, 150); + } + + function _addForestLossFilter(id, slug, category, name, options) { + var clickEvent = options.clickEvent || null, + disabled = options.disabled || false, + source = options.source || null, + category_color = options.category_color || '#ccc', + color = options.color || '#ccc', + subtitle = options.subtitle || ''; + + var cat = category.replace(/ /g, '_').toLowerCase(); + + if (!_.include(categories, cat)) { + var template = _.template($('#filter-template').html()), + $filter = $(template({ name: category, category: cat, data: cat, category_color: category_color })); + + $filters.find('filter-list').append($filter); + categories.push(cat); + } + + var layerItemTemplate = null, + $layerItem = null; + + layerItemTemplate = _.template($('#layer-item-checkbox-loss-template').html()); + + $layerItem = $(layerItemTemplate({ name: name, id: id, slug: slug, category: cat, disabled: disabled, source: source, color: color, subtitle: subtitle })); + + if (slug === 'loss' && config.BASELAYER === 'loss' || slug === 'forestgain' && _.include(config.MAPOPTIONS.layers, 596)) { + $layerItem.find('.checkbox').addClass('checked'); + + var color = $layerItem.find('.checkbox').attr('data-color'); + $layerItem.find('.checkbox').css('color', color ); + $layerItem.find('.checkbox').find('i').css('background-color', color ); + } + + $layerItem.find('.checkbox').on('click', function() { + clickEvent && clickEvent(); + }); + + $layer.find('.filter-links .extra').append($layerItem); + $layerItem.find('.checkbox').addClass(cat); + } + + function _addForestLossFilters(id, slug, category, name, options) { + var clickEvent = options.clickEvent || null, + disabled = options.disabled || false, + source = options.source || null, + category_color = options.category_color || '#ccc', + color = options.color || '#ccc', + subtitle = options.subtitle || ''; + + var cat = category.replace(/ /g, '_').toLowerCase(); + + if (!_.include(categories, cat)) { + var template = _.template($('#filter-template').html()), + $filter = $(template({ name: category, category: cat, data: cat, category_color: category_color })); + + $filters.find('ul').append($filter); + categories.push(cat); + } + + var layerItemTemplate = null, + $layerItem = null; + + layerItemTemplate = _.template($('#layer-item-radio-loss-template').html()); + + $layerItem = $(layerItemTemplate({ name: name, id: id, slug:slug, category: cat, disabled: disabled, source: source, subtitle: subtitle })); + + if (config.BASELAYER === 'loss' || _.include(config.MAPOPTIONS.layers, 596)) { + $layerItem.find('.radio').addClass('checked'); + } else { + $layerItem.find('.extra').hide(); + } + + $layerItem.find('.radio').on('click', function(e) { + e.preventDefault(); + + if (!$(this).hasClass('checked')) { + $layerItem.parent().find('.extra').slideUp(); + $layerItem.find('.extra').slideDown(150); + + setTimeout(function() { _resize(); }, 300); + + clickEvent && clickEvent(); + + $(this).parent().find('.checkbox').each(function(i, c) { + var $c = $(c); + $c.addClass('checked'); + var color = $c.attr('data-color'); + $c.css('color', color ); + $c.find('i').css('background-color', color ); + }); + } + }); + + $layer.find('.filter-links').append($layerItem); + $layerItem.find('.checkbox').addClass(cat); + } + + function _addFilter(id, slug, category, name, options) { + var clickEvent = options.clickEvent || null, + disabled = options.disabled || false; + source = options.source || null; + category_color = options.category_color || '#ccc'; + color = options.color || '#ccc'; + subtitle = options.subtitle || ''; + + var cat = category.replace(/ /g, '_').toLowerCase(); + + if (!_.include(categories, cat)) { + var template = _.template($('#filter-template').html()), + $filter = $(template({ name: category, category: cat, data: cat, category_color: category_color })); + + $filters.find('ul').append($filter); + categories.push(cat); + } + + var layerItemTemplate = null, + $layerItem = null; + + if (!disabled) { + // Select the kind of input (radio or checkbox) depending on the category + if (cat === 'forest_change') { + layerItemTemplate = _.template($('#layer-item-radio-template').html()); + + $layerItem = $(layerItemTemplate({ name: name, id: id, slug:slug, category: cat, disabled: disabled, source: source, subtitle: subtitle })); + + $layerItem.find('.radio').on('click', function() { + $layerItem.parent().find('.extra').slideUp(); + setTimeout(function() { _resize(); }, 300); + + if (!$(this).find('.radio').hasClass('checked')) { + clickEvent && clickEvent(); + } + + }); + } else { + layerItemTemplate = _.template($('#layer-item-checkbox-template').html()); + $layerItem = $(layerItemTemplate({ name: name, id: id, color: color, slug:slug, category: cat, disabled: disabled, source: source })); + + $layerItem.find('a:not(.source)').on('click', function() { + clickEvent(); + }); + } + } else { + layerItemTemplate = _.template($('#layer-item-disabled-template').html()); + $layerItem = $(layerItemTemplate({ name: name, id: id, color: color, slug:slug, category: cat, disabled: disabled, source: source })); + } + + if ((slug === 'nothing' && config.BASELAYER === null && !_.include(config.MAPOPTIONS.layers, 596)) || (slug === config.BASELAYER)) { + $layerItem.find('.radio').addClass('checked'); + } + + $layer.find('.filter-links').append($layerItem); + $layerItem.find('.checkbox').addClass(cat); + } + + return { + init: _init, + show: _show, + hide: _hide, + addFilter: _addFilter, + addForestLossFilters: _addForestLossFilters, + addForestLossFilter: _addForestLossFilter, + toggle: _toggle, + remove: _remove, + add: _add, + closeOpenFilter:_closeOpenFilter, + calcFiltersPosition: _calcFiltersPosition, + check: _check + }; + +}()); diff --git a/app/assets/stylesheets/_base.scss b/app/assets/stylesheets/_base.scss new file mode 100644 index 0000000000..afd5112610 --- /dev/null +++ b/app/assets/stylesheets/_base.scss @@ -0,0 +1,929 @@ +/* =Globals +----------------------------------------------- */ + +html { + @extend .sans-serif; +} + +body { + min-width: 980px; + border-top: 5px solid #222; + + &.embed { + min-width: 0; + #analysis_control{ + display: none; + } + .analysis_info{ + display: block; + .info_controls{ + display: none; + } + .spinner{ + display: none; + } + } + } +} + +a { + text-decoration: none; + color: $cGreen; + + &:hover { + color: darken($cGreen, 5%); + } +} + +em { + font-style: italic; +} + +strong { + font-weight: bold; +} + +.inner { + margin: 0 auto; + width: 960px; + padding: 0 10px; +} + +.serif { + font-family: "Georgia", serif; +} + +.sans-serif { + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; +} + +.futura { + font-family: "Futura", sans-serif; +} + +/* =Buttons +----------------------------------------------- */ + +.btn { + @include inline-block(); + border: 1px solid #839C26; + padding: 14px 34px; + color: #fff; + font-size: 11px; + font-weight: bold; + @extend .sans-serif; + text-decoration: none; + text-transform: uppercase; + background: #9bb251; + @include border-radius(2px); + cursor: pointer; + outline: none; + + &.red { + background: #AC2E25; + border-color: #8B1F16; + } + + &:hover { + color: #fff; + } + + &.dark_glow:hover { + @include box-shadow(0 0 6px 1px rgba(#000, .4)); + } + + &.darker_glow:hover { + @include box-shadow(0 0 6px 1px rgba(#000, .8)); + } + + &.disabled { + @include opacity(.5); + } +} + +.accept_btn, +.cancel_btn { + margin: 0; + padding: 13px 0; + width: 180px; + text-align: center; +} + +.cancel_btn { + margin-left: 20px; +} + + +/* =Sources +----------------------------------------------- */ + +#sources { + display: none; +} + +.sources_row { + display: table; + width: 100%; + + &.even { + background: #F2F2F3; + } + + dt { + display: table-cell; + width: 120px; + padding: 10px; + border-bottom: 1px solid #ccc; + font-weight: 500; + font-size: 11px; + @extend .sans-serif; + text-transform: uppercase; + } + + dd { + display: table-cell; + padding: 10px 10px 10px 15px; + border-left: 1px solid #ccc; + border-bottom: 1px solid #ccc; + font-size: 13px; + @extend .sans-serif; + + p { + margin-bottom: 10px; + + &:last-child { + margin-bottom: 0; + } + + sup { + position: relative; + top: -3px; + font-size: 80%; + } + } + } +} + +.single-source-item { + font-size: 15px; + line-height: 1.4; + color: #666; + + p { + margin-bottom: 10px; + + &:last-child { + margin-bottom: 0; + } + + &.credits { + margin-bottom: 10px; + font-size: 12px; + color: #aaa; + + strong { + display: block; + font-weight: bold; + } + + a { + color: #aaa; + text-decoration: underline; + + &:hover { + color: #aaa; + } + } + } + } +} + +.source_body, +.source_coverage { + @extend .single-source-item; + + .bullets li { + margin: 0 0 10px 20px; + padding: 0 0 0 20px; + background: image-url('icons/bullet.png') no-repeat 0 8px; + } + + .read_more { + padding: 10px 0; + font-size: 14px; + text-align: center; + } + + .hidden { + display: none; + } +} + +.backdrop { + display: none; + position: fixed; + z-index: 1050; + top: 0; left: 0; + width: 100%; + height: 100%; + background: #fff; + @include opacity(.9); +} + +.source_window { + $width: 497px; + $height: 300px; + + display: none; + position: fixed; + z-index: 1060; + left: 50%; top: 50%; + margin-left: -1*$width/2; + margin-top: -1*$height/2; + width: $width; + height: $height; + padding: 20px 15px 20px 20px; + background: #fff; + border: 1px solid #757573; + @include box-shadow(0px 0px 7px #666); + @include border-radius(3px); + + .close { + display: block; + position: absolute; + top: 10px; right: 15px; + width: 6px; + height: 6px; + @include icons-sprite(infowindow_close); + } + + .content { + height: $height; + width: 100%; + overflow: auto; + outline: none; + font-size: 15px; + @extend .sans-serif; + color: #666; + } + + .source_category_description, + .overview_title, + .source_download, + .source_extended, + .source_coverage_header, + .source_coverage, + .download { + display: none; + } + + .source_description { + display: block; + margin-top: 5px; + font-size: 13px; + line-height: 1.2; + @extend .sans-serif; + color: #999; + } + + .source_title, + .source_category_title, + .source_coverage_title { + display: block; + font-weight: bold; + font-size: 13px; + @extend .sans-serif; + text-transform: uppercase; + color: #aaa; + + span { + text-transform: none; + } + + sub { + position: relative; + bottom: -3px; + font-size: 80%; + } + } + + .source_header, + .source_category_title, + .source_coverage_header { + padding: 0 0 20px; + } + + .source_category_title { + border-bottom: 2px solid #ccc; + margin: 0 0 20px; + } + + .source_table { + border-top: 1px solid #ccc; + margin-bottom: 20px; + } + + .forest_change .source_category_title { + color: #F69; + border-bottom-color: #F69; + } + + .forest_cover .source_category_title { + color: #B2D26E; + border-bottom-color: #B2D26E; + } + + .forest_use .source_category_title { + color: #C98E6C; + border-bottom-color: #C98E6C; + } + + .conservation .source_category_title { + color: #3182BD; + border-bottom-color: #3182BD; + } + + .people .source_category_title { + color: #707D92; + border-bottom-color: #707D92; + } + + .stories .source_category_title { + color: #F2B257; + border-bottom-color: #F2B257; + } + + .source_category_title { + color: $cGreen; + border-bottom-color: $cGreen; + } + + .hidden { + display: block; + } + + .terms_inner .section { + padding: 20px 0 10px; + } + + .section-footer { + padding: 30px 0 0; + } + + .terms_footer { + margin-bottom: 30px; + } +} + +.jspDrag { + background: #EDEDED; + @include border-radius(3px); +} + +.jspHover, +.jspActive { + background: #ccc; +} + +.jspTrack, +.jspVerticalBar { + background: transparent; +} + +.jspTrack { + float: right; + width: 5px; +} + + +/* =Header +----------------------------------------------- */ + +#tpglt-nav-wrapper { + padding-top: 15px; + + a { color: #333; } +} + +.header { + overflow: hidden; + height: 226px; + background: image-url('backgrounds/bkg_header.png') repeat-x top center; +} + +.header-inner { + @extend .clearfix; + height: 100px; +} + +.feedback-link { + position: absolute; + z-index: 9999; + top: 50%; right: 0; + margin-top: -40px; + width: 30px; + border: 1px solid #697E1D; + border-right: 0; + height: 80px; + font-weight: bold; + font-size: 11px; + text-align: center; + text-transform: uppercase; + background: $cGreen; + + span { + display: block; + position: relative; + top: 20px; + color: #fff; + @include rotate(90deg); + } +} + +.header_separator { + display: inline-block; + position: absolute; + top: 106px; left: 0; + width: 100%; + border-bottom: 1px solid #CFCFCF; +} + +.header-logo { + display: inline-block; + position: relative; + z-index: 1050; + @include logos-sprite(logo); + margin-left: -10px; + width: 207px; + height: 100px; + text-indent: -99999px; +} + +.navbar { + float: right; + margin-top: 18px; + + ul, + #google_translate_element { + float: right; + margin-left: 10px; + } + + li { + float: left; + margin-left: 5px; + + a { + display: block; + height: 26px; + padding: 0 10px; + font-weight: bold; + font-size: 12px; + line-height: 27px; + text-transform: uppercase; + color: #333; + @include border-radius(3px); + + span { background:none; } + + &.selected { + background: $cGreen; + color: #fff; + + &:hover { + background: $cGreen; + } + } + + &:hover { + background: rgba($cGreen, .5); + } + } + } +} + +.header-title { + position: relative; + z-index: 1010; + font-size: 37px; + line-height: 1.1; + @extend .serif; + text-align: center; + color: #333; + + &.big { + font-size: 51px; + } + + a { + text-decoration: none; + } + + span { + color: $cGreen; + } +} + + +/* =Map +----------------------------------------------- */ + +.map-container { + position: relative; + width: 100%; +} + +#map { + position: relative; + width: 100%; + height: 400px; + border-top: 1px solid #717D80; + border-bottom: 1px solid #717D80; + @include user-select(none); +} + + +/* =Content +----------------------------------------------- */ + +.content { + @extend .clearfix; +} + +.section { + padding: 40px 0; + text-align: center; + + &.section_dark { + background: #222; + + .section-title { + color: #fff; + } + + .section-footer { + border-top: 1px solid #333; + } + } + + p { + font-size: 15px; + line-height: 1.2; + color: #666; + + a { + font-weight: bold; + } + } +} + +.section-title { + margin: 0 0 8px; + font-size: 37px; + @extend .serif; + color: #222; +} + +.columns { + @extend .clearfix; + padding: 40px 0; +} + +.column { + position: relative; + + &.three { + float: left; + display: table; + width: 266px; + margin: 0 27px; + } + + &.five { + display: inline-block; + height: 117px; + width: 117px; + margin: 0 27px; + } + + &.round { + height: 266px; + + img { + position: absolute; + z-index: 20; + top: 5px; left: 5px; + width: 256px; + height: 256px; + @include border-radius(129px); + } + + a { + display: table; + height: 100%; + width: 100%; + } + + .frame { + position: absolute; + top: 0; left: 0; + height: 100%; + width: 100%; + @include border-radius(134px); + background: #ccc; + } + + .gradient { + position: absolute; + top: 5px; left: 5px; + z-index: 50; + height: 256px; + width: 256px; + background-image: image-url('backgrounds/bkg_circles_gradient.png'); + @include border-radius(129px); + } + + .title { + display: table-cell; + position: relative; + z-index: 100; + vertical-align: middle; + + strong { + display: block; + margin: 0 30px 30px; + font-weight: 500; + font-size: 21px; + @extend .serif; + color: #fff; + } + + span { + display: block; + font-weight: bold; + font-size: 12px; + text-transform: uppercase; + color: $cGreen; + } + } + } +} + +.section-footer { + padding-top: 40px; + border-top: 1px solid #ccc; + + p + p { + margin-top: 15px; + } + + .btn + .btn { + margin-left: 20px; + } +} + + +/* =Footer +----------------------------------------------- */ + +.site-footer { + background-color: #464352; + + .right, + .left{ + float: right; + width: 49%; + } + .left{ + float: left; + .tweet-to{ + margin-top: 15px; + } + } +} + +.site-info { + @extend .clearfix; + font-size: 13px; + color: #b1afb6; + + a { + color: #eee; + text-decoration: underline; + + &:hover { + color: #fff; + } + } +} + + +.footer-share { + display: block; + margin-bottom: 30px; + height: 50px; + padding: 16px 0; + font-size: 17px; + font-family: "fira_sans_otregular"; + color: #fff; + + p { + float: left; + line-height: 45px; + + strong { + font-family: "fira_sans_otmedium"; + } + .subscribe{ + vertical-align: sub; + } + } + &.keep-updated{ + margin-bottom: 0; + height: 35px; + } +} + +.share_buttons { + float: left; + height: 27px; + padding: 12px 20px 6px; + background: #fff; + @include border-radius(23px); + + .fb_iframe_widget{ + top: -4px; + } +} + +.footer-logos { + float: left; + margin-top: 37px; + width: 480px; +} + +.footer-logos p { margin-bottom: 10px; } + +.footer-address { + margin: 60px 0 40px; + @extend .footer-logos; + @extend .sans-serif; + + p, + address, + .footer-logo { + margin-bottom: 20px; + } + + address { + line-height: 1.1; + } +} + +.footer-logo { + display: inline-block; + vertical-align: middle; + margin: 0 38px 38px 0; + width: 88px; + + &.wri { height: 31px; width: 269px; @include logos-sprite(wri); } + &.google { height: 52px; @include logos-sprite(google); } + &.esri { height: 28px; @include logos-sprite(esri); } + &.universitymaryland { height: 52px; @include logos-sprite(universitymaryland); } + &.unep { height: 52px; @include logos-sprite(unep); } + &.imazon { height: 52px; @include logos-sprite(imazon); } + &.cfgd { height: 52px; @include logos-sprite(cfgd); } + &.osfac { height: 57px; @include logos-sprite(osfac); } + &.gfwca { height: 44px; @include logos-sprite(gfwca); } + &.scanex { height: 16px; @include logos-sprite(scannex); } + &.twrus { height: 36px; width: 147px; @include logos-sprite(twrus); } + &.norwegian { height: 55px; @include logos-sprite(norwegian); } + &.usaid { height: 64px; @include logos-sprite(usaid); } + &.gef { height: 59px; @include logos-sprite(gef); } + &.ukaid { height: 52px; @include logos-sprite(ukaid); } + &.tilia { height: 38px; @include logos-sprite(tilia); } + &.goodall { height: 36px; width: 147px; @include logos-sprite(goodall); } + &.cartodb { height: 45px; @include logos-sprite(cartodb); } + &.last { margin-right: 0; } +} + + +/* =Carrousel +----------------------------------------------- */ + +.carrousel { + position: relative; + margin: 0 0 40px; + height: 362px; + text-align: left; + + .frame { + display:block; + position: absolute; + z-index: 20; + bottom: 43px; + right: 43px; + width: 266px; + height: 266px; + @include border-radius(134px); + background: #333; + background: rgba(#000, .5); + + img { + position: absolute; + top: 5px; left: 5px; + width: 256px; + height: 256px; + @include border-radius(129px); + } + } +} + +.previous, +.next { + position: absolute; + display: block; + width: 68px; + height: 68px; +} + +.previous { + top: 30px; + left: -34px; + @include icons-sprite(button_story_gallery_left); + z-index: 100; +} + +.next { + bottom: 30px; + right: -25px; + @include icons-sprite(button_story_gallery_right); + z-index: 100; +} + +.slide { + display: none; + overflow: hidden; + position: absolute; + z-index: 10; + top: 0; left: 0; + width: 100%; + + img { width: 100%; } +} + + +/* =Dropdowns +----------------------------------------------- */ + +.qtip-default { + border: 1px solid #ccc; + background: #fff; + @include border-radius(3px); + @include box-shadow(0 0 3px rgba(#000, .2)); +} + +.qtip-content { + padding: 0; + + li { + &:first-child a { + border-top: 0; + @include border-top-radius(2px); + } + + &:last-child a { + @include border-bottom-radius(2px); + } + + a { + display: block; + border-top: 1px solid #ddd; + padding: 10px; + min-width: 80px; + font-size: 13px; + @extend .serif; + color: #666; + + &:hover { + background: #fafafa; + text-decoration: underline; + } + } + } +} + +/* =Share embed +----------------------------------------------- */ +.share_control { + position: relative; + display: block; + width: 38px; + height: 38px; + left: 980px; + top: -3px; + @include icons-sprite(share_button); + + &:hover { @include icons-sprite(share_button_hover); } + &:active { @include icons-sprite(share_button_active); } +} \ No newline at end of file diff --git a/app/assets/stylesheets/map.css.scss b/app/assets/stylesheets/_grid.scss similarity index 100% rename from app/assets/stylesheets/map.css.scss rename to app/assets/stylesheets/_grid.scss diff --git a/app/assets/stylesheets/_layout.scss b/app/assets/stylesheets/_layout.scss new file mode 100644 index 0000000000..e69de29bb2 diff --git a/app/assets/stylesheets/_mixins.scss b/app/assets/stylesheets/_mixins.scss new file mode 100644 index 0000000000..6bcf560d57 --- /dev/null +++ b/app/assets/stylesheets/_mixins.scss @@ -0,0 +1,344 @@ +/**********/ +/* Colors */ +/**********/ +$cBackground: #222222; +$cDarkGreen: #264C59; +$cLink: #A1BA42; +$cGreen2: #9FB840; +$cGreen1: #A1BA42; +$cGreen: #74ACB4; +$cGold: #F2B257; +$cLime: #A1BA42; +$cDarkGold: #D69E4D; + +$cWhite: #FFFFFF; +$cGray8: #393939; +$cGray7: #444444; +$cGray6: #797979; +$cGray5: #222222; +$cGray4: #CCCCCC; +$cGray3: #999999; +$cGray2: #666666; +$cGray1: #333333; +$cGray0: #000000; + +$cSeparator: #E5E5E5; + +$cStories: $cGold; +$cStoriesHover: $cDarkGold; + +$cForestUse: #CC4C02; +$cForestUseHover: darken(#CC4C02, 15%); + +$cForestClearing: #F69; +$cForestClearingHover: darken(#F69, 15%); + +$cConservation: #00CED0; // blue +$cConservationHover: #00B9B9; + +$cOtherLayers: #00CED0; // blue +$cOtherLayersHover: #00B9B9; + +$cMining: $cGray4; +$cMiningHover: $cGray3; + +$cForest: #707D92; +$cForestHover: #707D92; + +$cSpecialProjects: #594694; +$cSpecialProjectsHover: #594694; + +$cRegrowth: #B2D26E; // green +$cRegrowthHover: #9EBB62; // dark green + +$cFire: #E95850; // red +$cFireHover: #CA4C45; + +$cDeforestation: #FF6699; // red +$cDeforestationHover: #C04B73; + +$cTop: #222222; +$cPostTitle: #73ACB3; // green +$cPostBody: #666666; + +// This is only used in the demo page +.cforest { background-color: $cForest; } +.cBackground { background-color: $cBackground; } +.cDarkGreen { background-color: $cDarkGreen; } +.cLink { background-color: $cLink; } +.cGreen { background-color: $cGreen; } +.cWhite { background-color: $cWhite; } +.cGray4 { background-color: $cGray4; } +.cGray3 { background-color: $cGray3; } +.cGray2 { background-color: $cGray2; } +.cGray1 { background-color: $cGray1; } +.cGray0 { background-color: $cGray0; } +.cGold { background-color: $cGold; } + +.cStories { background-color: $cStories; } +.cStoriesHover { background-color: $cStoriesHover; } + +.cForestUse { background-color: $cForestUse; } +.cForestUseHover { background-color: $cForestUseHover; } + +.cConservation { background-color: $cConservation; } +.cMining { background-color: $cMining; } + +.cForest { background-color: $cForest; } +.cForestHover { background-color: $cForestHover; } + +.cFire { background-color: $cFire; } + +.cTop { background-color: $cTop; } +.cPostTitle { background-color: $cPostTitle; } +.cPostBody { background-color: $cPostBody; } + +// SPRITES +$icons-community-spacing:90px; +$icons-analysis-spacing:90px; +$icons-updates-spacing:90px; + +$icons-checkbox-spacing:20px; +$icons-checkbox_checked-spacing:20px; +$icons-checkbox_hover-spacing:20px; + +$icons-input_error-spacing:20px; + +$icons-download_arrow-spacing:20px; +$icons-download_arrow_hover-spacing:20px; +$icons-bars-spacing:20px; + +$icons-forest_use-spacing:20px; +$icons-conservation-spacing: 20px; +$icons-mining-spacing: 20px; +$icons-fire-spacing: 20px; + +// Radio buttons & checkboxes +$icons-radio-spacing: 41px; +$icons-radio_fire-spacing: 44px; +$icons-checkboxes-spacing: 40px; +$icons-checkboxes_mining-spacing: 40px; +$icons-checkboxes_regrowth-spacing: 40px; +$icons-checkboxes_forest_use-spacing: 40px; +$icons-checkboxes_forest-spacing: 40px; +$icons-checkboxes_mining-spacing: 40px; +$icons-checkboxes_other_layers-spacing: 40px; + +$icons-bullet_square-spacing: 40px; + +// Country +$icons-country_bolivia_disabled-spacing: 20px; + +@import "icons/*.png"; /**/ +@import "logos/*.png"; /**/ + +@import "mixins/*.png"; /**/ + +$countries-spacing: 20px; + +strong { font-weight: bold; } // This is sad, I know. + +.hidden { display:none; } + +.classic-hover { + text-decoration: none; + + &:hover { + text-decoration: underline; + } +} + +.pull-right { + float:right; +} + +.opacity-transition { + @include transition-property(opacity); + @include transition-duration(250ms); + @include transition-timing-function(ease-in); +} + +.position-transition { + @include transition-property(position); + @include transition-duration(250ms); + @include transition-timing-function(ease-in); +} + +.shadow-transition { + -webkit-transition: -webkit-box-shadow 0.3s ease-out; + -moz-transition: -moz-box-shadow 0.3s ease-out; + -o-transition: box-shadow 0.3s ease-out; +} + +.no-glow { + -moz-box-shadow: none; + -webkit-box-shadow: none; + box-shadow: none; +} + +@mixin dark-glow($opacity) { + -moz-box-shadow:0px 0px 3px 5px rgba(0, 0, 0, $opacity); /* Firefox v3.5+ */ + -webkit-box-shadow:0px 0px 3px 5px rgba(0,0,0, $opacity); /* Safari v3.0+ and by Chrome v0.2+ */ + box-shadow:0px 0px 3px 1px rgba(0,0,0, $opacity); /* Firefox v4.0+ , Safari v5.1+ , Chrome v10.0+ and by Opera v10.5+ */ + -ms-filter:"progid:DXImageTransform.Microsoft.Glow(Color = #33000000 ,Strength = $opacity * 10)"; + filter:progid:DXImageTransform.Microsoft.Glow(Color = #33000000 ,Strength = $opacity * 10); +} +@mixin darker-glow($opacity) { + -moz-box-shadow:0px 0px 6px 1px rgba(0, 0, 0, $opacity); /* Firefox v3.5+ */ + -webkit-box-shadow:0px 0px 6px 1px rgba(0, 0, 0, $opacity); /* Safari v3.0+ and by Chrome v0.2+ */ + box-shadow:0px 0px 6px 1px rgba(0, 0, 0, $opacity); /* Firefox v4.0+ , Safari v5.1+ , Chrome v10.0+ and by Opera v10.5+ */ + -ms-filter:"progid:DXImageTransform.Microsoft.Glow(Color = #33000000 ,Strength = $opacity * 10)"; + filter:progid:DXImageTransform.Microsoft.Glow(Color = #33000000 ,Strength = $opacity * 10); +} + +@mixin button($url, $height, $mL, $pT, $pR, $pB, $pL, $offset-y) { + @include inline-block(); + + position:relative; + + height:$height; + background: url($url) no-repeat left -1*($offset-y + $height); + color:#fff; + text-decoration:none; + text-transform: uppercase; + + @extend .shadow-transition; + + & > span { + @include inline-block(); + height: $height - $pT - $pB; + margin-left: $mL; + padding: $pT $pR $pB $pL; + background: url($url) repeat-x right -1*$offset-y; + white-space:nowrap; + } +} + +@mixin size($width, $height:$width) { + + @if $width != false { width: $width; } + @if $height != false { height: $height; } + +} + +@mixin position($top, $right, $bottom, $left) { + + position: absolute; + + @if $top != false { top: $top; } + @if $right != false { right: $right; } + @if $bottom != false { bottom: $bottom; } + @if $left != false { left: $left; } + +} + +@mixin absolute-portrait-center($height) { + position:absolute; + top:50%; + height: $height; + margin-top: -1*$height/2; +} + +@mixin absolute-landscape-center($width) { + @include absolute-left-center($width); +} + +@mixin absolute-left-center($width) { + position:absolute; + left:50%; + width: $width; + margin-left: -1*$width/2; +} + +@mixin absolute-center($width, $height:$width) { + + position:absolute; + left:50%; + top: 50%; + + margin-left: -1*$width/2; + margin-top: -1*$height/2; + + @if $width != false { width: $width; } + @if $height != false { height: $height; } + +} + +@mixin clearfix() { + & { *zoom: 1; } + &:before, &:after { content: ""; display: table; } + &:after { clear: both; } +} + +.disable-selection { + -webkit-touch-callout: none; + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} + +@mixin disabled($url) { + @include inline-block(); + @include icons-sprite(disabled); + + padding-left: 22px; + + text-decoration:none; +} + +// Fonts +.serif { + font-family: "Georgia", serif; +} +.sans-serif { + font-family: Helvetica, Arial, sans-serif; +} + +.font-big { + @extend .serif; + font-size: 37px; + color: $cWhite; +} + +.font-regular { + @extend .sans-serif; + font-size: 15px; + color: $cWhite; +} + +.font-golden { + @extend .sans-serif; + font-size: 12px; + font-weight:bold; + color: $cLime; + text-transform: uppercase; + text-decoration: none; + + &:hover { + color: $cGray2; + } +} + +@mixin pointer-events($type: none) { + $type: unquote($type); + @include experimental(pointer-events, $type, + -moz, -webkit, not -o, not -ms, -khtml, official + ); +} + +.clearfix { + zoom:1; + + &:before, &:after { + content: "\0020"; + display: block; + height: 0; + overflow: hidden; + } + + &:after { + clear: both; + } +} \ No newline at end of file diff --git a/app/assets/stylesheets/fonts.scss b/app/assets/stylesheets/_settings.scss similarity index 88% rename from app/assets/stylesheets/fonts.scss rename to app/assets/stylesheets/_settings.scss index ba99b110b0..6ad0cb8eab 100644 --- a/app/assets/stylesheets/fonts.scss +++ b/app/assets/stylesheets/_settings.scss @@ -1,44 +1,32 @@ -@font-face { - font-family: 'fira_sans_otregular'; - src: font_url('firasansot-regular-webfont.eot'); - src: font_url('firasansot-regular-webfont.eot?#iefix') format('embedded-opentype'), - font_url('firasansot-regular-webfont.woff') format('woff'), - font_url('firasansot-regular-webfont.ttf') format('truetype'), - font_url('firasansot-regular-webfont.svg#fira_sans_otregular') format('svg'); - font-weight: normal; - font-style: normal; -} - -@font-face { - font-family: 'fira_sans_otmedium'; - src: font_url('firasansot-medium-webfont.eot'); - src: font_url('firasansot-medium-webfont.eot?#iefix') format('embedded-opentype'), - font_url('firasansot-medium-webfont.woff') format('woff'), - font_url('firasansot-medium-webfont.ttf') format('truetype'), - font_url('firasansot-medium-webfont.svg#fira_sans_otmedium') format('svg'); - font-weight: normal; - font-style: normal; -} - -@font-face { - font-family: 'fira_sans_otlight'; - src: font_url('firasansot-light-webfont.eot'); - src: font_url('firasansot-light-webfont.eot?#iefix') format('embedded-opentype'), - font_url('firasansot-light-webfont.woff') format('woff'), - font_url('firasansot-light-webfont.ttf') format('truetype'), - font_url('firasansot-light-webfont.svg#fira_sans_otmedium') format('svg'); - font-weight: normal; - font-style: normal; -} - -.serif { - font-family: "Georgia", serif; -} - -.sans-serif { - font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; -} - -.futura { - font-family: "Futura", sans-serif; -} +@font-face { + font-family: 'fira_sans_otregular'; + src: font_url('firasansot-regular-webfont.eot'); + src: font_url('firasansot-regular-webfont.eot?#iefix') format('embedded-opentype'), + font_url('firasansot-regular-webfont.woff') format('woff'), + font_url('firasansot-regular-webfont.ttf') format('truetype'), + font_url('firasansot-regular-webfont.svg#fira_sans_otregular') format('svg'); + font-weight: normal; + font-style: normal; +} + +@font-face { + font-family: 'fira_sans_otmedium'; + src: font_url('firasansot-medium-webfont.eot'); + src: font_url('firasansot-medium-webfont.eot?#iefix') format('embedded-opentype'), + font_url('firasansot-medium-webfont.woff') format('woff'), + font_url('firasansot-medium-webfont.ttf') format('truetype'), + font_url('firasansot-medium-webfont.svg#fira_sans_otmedium') format('svg'); + font-weight: normal; + font-style: normal; +} + +@font-face { + font-family: 'fira_sans_otlight'; + src: font_url('firasansot-light-webfont.eot'); + src: font_url('firasansot-light-webfont.eot?#iefix') format('embedded-opentype'), + font_url('firasansot-light-webfont.woff') format('woff'), + font_url('firasansot-light-webfont.ttf') format('truetype'), + font_url('firasansot-light-webfont.svg#fira_sans_otmedium') format('svg'); + font-weight: normal; + font-style: normal; +} \ No newline at end of file diff --git a/app/assets/stylesheets/application.scss b/app/assets/stylesheets/application.scss index d6b57c6063..62767d2f18 100644 --- a/app/assets/stylesheets/application.scss +++ b/app/assets/stylesheets/application.scss @@ -1,939 +1,35 @@ //= require jquery.qtip.min //= require jquery.jscrollpane - +//= require tipsy @import 'compass/reset'; +@import "compass"; +@import "compass/css3"; @import 'compass/css3/border-radius'; @import 'compass/css3/box-shadow'; @import 'compass/css3/opacity'; @import 'compass/css3/user-interface'; @import 'compass/css3/inline-block'; +@import 'compass/css3/text-shadow'; @import 'compass/css3/transform'; +@import 'compass/css3/background-size'; +@import 'compass/css3/images'; -@import 'fonts'; -@import 'helpers'; +@import '_settings'; +@import '_mixins'; +@import '_layout'; +@import '_grid'; +@import '_base'; @import 'icons/*.png'; -/**/ @import 'logos/*.png'; -/**/ - - -/* =Globals ------------------------------------------------ */ - -html { - @extend .sans-serif; -} - -body { - min-width: 980px; - border-top: 5px solid #222; - - &.embed { - min-width: 0; - #analysis_control{ - display: none; - } - .analysis_info{ - display: block; - .info_controls{ - display: none; - } - .spinner{ - display: none; - } - } - } -} - -a { - text-decoration: none; - color: $cGreen; - - &:hover { - color: darken($cGreen, 5%); - } -} - -em { - font-style: italic; -} - -strong { - font-weight: bold; -} - -.inner { - margin: 0 auto; - width: 960px; - padding: 0 10px; -} - - -/* =Buttons ------------------------------------------------ */ - -.btn { - @include inline-block(); - border: 1px solid #839C26; - padding: 14px 34px; - color: #fff; - font-size: 11px; - font-weight: bold; - @extend .sans-serif; - text-decoration: none; - text-transform: uppercase; - background: #9bb251; - @include border-radius(2px); - cursor: pointer; - outline: none; - - &.red { - background: #AC2E25; - border-color: #8B1F16; - } - - &:hover { - color: #fff; - } - - &.dark_glow:hover { - @include box-shadow(0 0 6px 1px rgba(#000, .4)); - } - - &.darker_glow:hover { - @include box-shadow(0 0 6px 1px rgba(#000, .8)); - } - - &.disabled { - @include opacity(.5); - } -} - -.accept_btn, -.cancel_btn { - margin: 0; - padding: 13px 0; - width: 180px; - text-align: center; -} - -.cancel_btn { - margin-left: 20px; -} - - -/* =Sources ------------------------------------------------ */ - -#sources { - display: none; -} - -.sources_row { - display: table; - width: 100%; - - &.even { - background: #F2F2F3; - } - - dt { - display: table-cell; - width: 120px; - padding: 10px; - border-bottom: 1px solid #ccc; - font-weight: 500; - font-size: 11px; - @extend .sans-serif; - text-transform: uppercase; - } - - dd { - display: table-cell; - padding: 10px 10px 10px 15px; - border-left: 1px solid #ccc; - border-bottom: 1px solid #ccc; - font-size: 13px; - @extend .sans-serif; - - p { - margin-bottom: 10px; - - &:last-child { - margin-bottom: 0; - } - - sup { - position: relative; - top: -3px; - font-size: 80%; - } - } - } -} - -.single-source-item { - font-size: 15px; - line-height: 1.4; - color: #666; - - p { - margin-bottom: 10px; - - &:last-child { - margin-bottom: 0; - } - - &.credits { - margin-bottom: 10px; - font-size: 12px; - color: #aaa; - - strong { - display: block; - font-weight: bold; - } - - a { - color: #aaa; - text-decoration: underline; - - &:hover { - color: #aaa; - } - } - } - } -} - -.source_body, -.source_coverage { - @extend .single-source-item; - - .bullets li { - margin: 0 0 10px 20px; - padding: 0 0 0 20px; - background: image-url('icons/bullet.png') no-repeat 0 8px; - } - - .read_more { - padding: 10px 0; - font-size: 14px; - text-align: center; - } - - .hidden { - display: none; - } -} - -.backdrop { - display: none; - position: fixed; - z-index: 1050; - top: 0; left: 0; - width: 100%; - height: 100%; - background: #fff; - @include opacity(.9); -} - -.source_window { - $width: 497px; - $height: 300px; - - display: none; - position: fixed; - z-index: 1060; - left: 50%; top: 50%; - margin-left: -1*$width/2; - margin-top: -1*$height/2; - width: $width; - height: $height; - padding: 20px 15px 20px 20px; - background: #fff; - border: 1px solid #757573; - @include box-shadow(0px 0px 7px #666); - @include border-radius(3px); - - .close { - display: block; - position: absolute; - top: 10px; right: 15px; - width: 6px; - height: 6px; - @include icons-sprite(infowindow_close); - } - - .content { - height: $height; - width: 100%; - overflow: auto; - outline: none; - font-size: 15px; - @extend .sans-serif; - color: #666; - } - - .source_category_description, - .overview_title, - .source_download, - .source_extended, - .source_coverage_header, - .source_coverage, - .download { - display: none; - } - - .source_description { - display: block; - margin-top: 5px; - font-size: 13px; - line-height: 1.2; - @extend .sans-serif; - color: #999; - } - - .source_title, - .source_category_title, - .source_coverage_title { - display: block; - font-weight: bold; - font-size: 13px; - @extend .sans-serif; - text-transform: uppercase; - color: #aaa; - - span { - text-transform: none; - } - - sub { - position: relative; - bottom: -3px; - font-size: 80%; - } - } - - .source_header, - .source_category_title, - .source_coverage_header { - padding: 0 0 20px; - } - - .source_category_title { - border-bottom: 2px solid #ccc; - margin: 0 0 20px; - } - - .source_table { - border-top: 1px solid #ccc; - margin-bottom: 20px; - } - - .forest_change .source_category_title { - color: #F69; - border-bottom-color: #F69; - } - - .forest_cover .source_category_title { - color: #B2D26E; - border-bottom-color: #B2D26E; - } - - .forest_use .source_category_title { - color: #C98E6C; - border-bottom-color: #C98E6C; - } - - .conservation .source_category_title { - color: #3182BD; - border-bottom-color: #3182BD; - } - - .people .source_category_title { - color: #707D92; - border-bottom-color: #707D92; - } - - .stories .source_category_title { - color: #F2B257; - border-bottom-color: #F2B257; - } - - .source_category_title { - color: $cGreen; - border-bottom-color: $cGreen; - } - - .hidden { - display: block; - } - - .terms_inner .section { - padding: 20px 0 10px; - } - - .section-footer { - padding: 30px 0 0; - } - - .terms_footer { - margin-bottom: 30px; - } -} - -.jspDrag { - background: #EDEDED; - @include border-radius(3px); -} - -.jspHover, -.jspActive { - background: #ccc; -} - -.jspTrack, -.jspVerticalBar { - background: transparent; -} - -.jspTrack { - float: right; - width: 5px; -} - - -/* =Header ------------------------------------------------ */ - -#tpglt-nav-wrapper { - padding-top: 15px; - - a { color: #333; } -} - -.header { - overflow: hidden; - height: 226px; - background: image-url('backgrounds/bkg_header.png') repeat-x top center; -} - -.header-inner { - @extend .clearfix; - height: 100px; -} - -.feedback-link { - position: absolute; - z-index: 9999; - top: 50%; right: 0; - margin-top: -40px; - width: 30px; - border: 1px solid #697E1D; - border-right: 0; - height: 80px; - font-weight: bold; - font-size: 11px; - text-align: center; - text-transform: uppercase; - background: $cGreen; - - span { - display: block; - position: relative; - top: 20px; - color: #fff; - @include rotate(90deg); - } -} - -.header_separator { - display: inline-block; - position: absolute; - top: 106px; left: 0; - width: 100%; - border-bottom: 1px solid #CFCFCF; -} - -.header-logo { - display: inline-block; - position: relative; - z-index: 1050; - @include logos-sprite(logo); - margin-left: -10px; - width: 207px; - height: 100px; - text-indent: -99999px; -} - -.navbar { - float: right; - margin-top: 18px; - - ul, - #google_translate_element { - float: right; - margin-left: 10px; - } - - li { - float: left; - margin-left: 5px; - - a { - display: block; - height: 26px; - padding: 0 10px; - font-weight: bold; - font-size: 12px; - line-height: 27px; - text-transform: uppercase; - color: #333; - @include border-radius(3px); - - span { background:none; } - - &.selected { - background: $cGreen; - color: #fff; - - &:hover { - background: $cGreen; - } - } - - &:hover { - background: rgba($cGreen, .5); - } - } - } -} - -.header-title { - position: relative; - z-index: 1010; - font-size: 37px; - line-height: 1.1; - @extend .serif; - text-align: center; - color: #333; - - &.big { - font-size: 51px; - } - - a { - text-decoration: none; - } - - span { - color: $cGreen; - } -} - - -/* =Map ------------------------------------------------ */ - -.map-container { - position: relative; - width: 100%; -} - -#map { - position: relative; - width: 100%; - height: 400px; - border-top: 1px solid #717D80; - border-bottom: 1px solid #717D80; - @include user-select(none); -} - - -/* =Content ------------------------------------------------ */ - -.content { - @extend .clearfix; -} - -.section { - padding: 40px 0; - text-align: center; - - &.section_dark { - background: #222; - - .section-title { - color: #fff; - } - - .section-footer { - border-top: 1px solid #333; - } - } - - p { - font-size: 15px; - line-height: 1.2; - color: #666; - - a { - font-weight: bold; - } - } -} - -.section-title { - margin: 0 0 8px; - font-size: 37px; - @extend .serif; - color: #222; -} - -.columns { - @extend .clearfix; - padding: 40px 0; -} - -.column { - position: relative; - - &.three { - float: left; - display: table; - width: 266px; - margin: 0 27px; - } - - &.five { - display: inline-block; - height: 117px; - width: 117px; - margin: 0 27px; - } - - &.round { - height: 266px; - - img { - position: absolute; - z-index: 20; - top: 5px; left: 5px; - width: 256px; - height: 256px; - @include border-radius(129px); - } - - a { - display: table; - height: 100%; - width: 100%; - } - - .frame { - position: absolute; - top: 0; left: 0; - height: 100%; - width: 100%; - @include border-radius(134px); - background: #ccc; - } - - .gradient { - position: absolute; - top: 5px; left: 5px; - z-index: 50; - height: 256px; - width: 256px; - background-image: image-url('backgrounds/bkg_circles_gradient.png'); - @include border-radius(129px); - } - - .title { - display: table-cell; - position: relative; - z-index: 100; - vertical-align: middle; - - strong { - display: block; - margin: 0 30px 30px; - font-weight: 500; - font-size: 21px; - @extend .serif; - color: #fff; - } - - span { - display: block; - font-weight: bold; - font-size: 12px; - text-transform: uppercase; - color: $cGreen; - } - } - } -} - -.section-footer { - padding-top: 40px; - border-top: 1px solid #ccc; - - p + p { - margin-top: 15px; - } - - .btn + .btn { - margin-left: 20px; - } -} - - -/* =Footer ------------------------------------------------ */ - -.site-footer { - background-color: #464352; - - .right, - .left{ - float: right; - width: 49%; - } - .left{ - float: left; - .tweet-to{ - margin-top: 15px; - } - } -} - -.site-info { - @extend .clearfix; - font-size: 13px; - color: #b1afb6; - - a { - color: #eee; - text-decoration: underline; - - &:hover { - color: #fff; - } - } -} - - -.footer-share { - display: block; - margin-bottom: 30px; - height: 50px; - padding: 16px 0; - font-size: 17px; - font-family: "fira_sans_otregular"; - color: #fff; - - p { - float: left; - line-height: 45px; - - strong { - font-family: "fira_sans_otmedium"; - } - .subscribe{ - vertical-align: sub; - } - } - &.keep-updated{ - margin-bottom: 0; - height: 35px; - } -} - -.share_buttons { - float: left; - height: 27px; - padding: 12px 20px 6px; - background: #fff; - @include border-radius(23px); - - .fb_iframe_widget{ - top: -4px; - } -} - -.footer-logos { - float: left; - margin-top: 37px; - width: 480px; -} - -.footer-logos p { margin-bottom: 10px; } - -.footer-address { - margin: 60px 0 40px; - @extend .footer-logos; - @extend .sans-serif; - - p, - address, - .footer-logo { - margin-bottom: 20px; - } - - address { - line-height: 1.1; - } -} - -.footer-logo { - display: inline-block; - vertical-align: middle; - margin: 0 38px 38px 0; - width: 88px; - - &.wri { height: 31px; width: 269px; @include logos-sprite(wri); } - &.google { height: 52px; @include logos-sprite(google); } - &.esri { height: 28px; @include logos-sprite(esri); } - &.universitymaryland { height: 52px; @include logos-sprite(universitymaryland); } - &.unep { height: 52px; @include logos-sprite(unep); } - &.imazon { height: 52px; @include logos-sprite(imazon); } - &.cfgd { height: 52px; @include logos-sprite(cfgd); } - &.osfac { height: 57px; @include logos-sprite(osfac); } - &.gfwca { height: 44px; @include logos-sprite(gfwca); } - &.scanex { height: 16px; @include logos-sprite(scannex); } - &.twrus { height: 36px; width: 147px; @include logos-sprite(twrus); } - &.norwegian { height: 55px; @include logos-sprite(norwegian); } - &.usaid { height: 64px; @include logos-sprite(usaid); } - &.gef { height: 59px; @include logos-sprite(gef); } - &.ukaid { height: 52px; @include logos-sprite(ukaid); } - &.tilia { height: 38px; @include logos-sprite(tilia); } - &.goodall { height: 36px; width: 147px; @include logos-sprite(goodall); } - &.cartodb { height: 45px; @include logos-sprite(cartodb); } - &.last { margin-right: 0; } -} - - -/* =Carrousel ------------------------------------------------ */ - -.carrousel { - position: relative; - margin: 0 0 40px; - height: 362px; - text-align: left; - - .frame { - display:block; - position: absolute; - z-index: 20; - bottom: 43px; - right: 43px; - width: 266px; - height: 266px; - @include border-radius(134px); - background: #333; - background: rgba(#000, .5); - - img { - position: absolute; - top: 5px; left: 5px; - width: 256px; - height: 256px; - @include border-radius(129px); - } - } -} - -.previous, -.next { - position: absolute; - display: block; - width: 68px; - height: 68px; -} - -.previous { - top: 30px; - left: -34px; - @include icons-sprite(button_story_gallery_left); - z-index: 100; -} - -.next { - bottom: 30px; - right: -25px; - @include icons-sprite(button_story_gallery_right); - z-index: 100; -} - -.slide { - display: none; - overflow: hidden; - position: absolute; - z-index: 10; - top: 0; left: 0; - width: 100%; - - img { width: 100%; } -} - - -/* =Dropdowns ------------------------------------------------ */ - -.qtip-default { - border: 1px solid #ccc; - background: #fff; - @include border-radius(3px); - @include box-shadow(0 0 3px rgba(#000, .2)); -} - -.qtip-content { - padding: 0; - - li { - &:first-child a { - border-top: 0; - @include border-top-radius(2px); - } - - &:last-child a { - @include border-bottom-radius(2px); - } - - a { - display: block; - border-top: 1px solid #ddd; - padding: 10px; - min-width: 80px; - font-size: 13px; - @extend .serif; - color: #666; - - &:hover { - background: #fafafa; - text-decoration: underline; - } - } - } -} - -/* =Share embed ------------------------------------------------ */ -.share_control { - position: relative; - display: block; - width: 38px; - height: 38px; - left: 980px; - top: -3px; - @include icons-sprite(share_button); - - &:hover { @include icons-sprite(share_button_hover); } - &:active { @include icons-sprite(share_button_active); } -} \ No newline at end of file +@import 'home-icons/*.png'; +@import 'static-icons/*.png'; +@import 'static-logos/*.png'; +@import 'country-icons/*.png'; +@import 'stories-icons/*.png'; + +@import 'modules/_mod-form'; +@import 'modules/_mod-map'; +@import 'modules/countries'; +@import 'modules/home'; \ No newline at end of file diff --git a/app/assets/stylesheets/helpers.scss b/app/assets/stylesheets/helpers.scss deleted file mode 100644 index cad09b7919..0000000000 --- a/app/assets/stylesheets/helpers.scss +++ /dev/null @@ -1,24 +0,0 @@ -$cGreen: #A1BA42; - -.clearfix { - zoom:1; - - &:before, &:after { - content: "\0020"; - display: block; - height: 0; - overflow: hidden; - } - - &:after { - clear: both; - } -} - -@mixin pointer-events($type: none) { - $type: unquote($type); - - @include experimental(pointer-events, $type, - -moz, -webkit, not -o, not -ms, -khtml, official - ); -} diff --git a/app/assets/stylesheets/forms.scss b/app/assets/stylesheets/modules/_mod-form.scss similarity index 92% rename from app/assets/stylesheets/forms.scss rename to app/assets/stylesheets/modules/_mod-form.scss index 1df2aa14fc..27c7a62a68 100644 --- a/app/assets/stylesheets/forms.scss +++ b/app/assets/stylesheets/modules/_mod-form.scss @@ -1,7 +1,3 @@ -@import 'compass/css3/border-radius'; -@import 'compass/css3/inline-block'; - - .field { margin: 0 0 25px; } diff --git a/app/assets/stylesheets/modules/_mod-map.scss b/app/assets/stylesheets/modules/_mod-map.scss new file mode 100644 index 0000000000..40f4874a7e --- /dev/null +++ b/app/assets/stylesheets/modules/_mod-map.scss @@ -0,0 +1,1615 @@ +#zoom_controls, +#zoom_controls_subscribe { + + $zoomWidth: 36px; + $zoomHeight: 29px; // height of the top button + border line + + position:absolute; + + left: 15px; + top: 30px; + + z-index: 1000; + + .zoom_in, .zoom_out { position:relative; width: $zoomWidth; height: $zoomHeight; cursor: pointer; } + + .zoom_in { + background: transparent url(icons/map_zoom.png) no-repeat 0 0; + &:hover { background: transparent url(icons/map_zoom.png) no-repeat -1*$zoomWidth 0; } + &:active { background: transparent url(icons/map_zoom.png) no-repeat -2*$zoomWidth 0; } + } + + .zoom_out { + background: transparent url(icons/map_zoom.png) no-repeat 0 -1*$zoomHeight; + &:hover { background: transparent url(icons/map_zoom.png) no-repeat -1*$zoomWidth -1*$zoomHeight; } + &:active { background: transparent url(icons/map_zoom.png) no-repeat -2*$zoomWidth -1*$zoomHeight; } + } +} + +#zoom_controls, +#viewfinder, +.map_coordinates { + display: none; +} + +#spinner_tiles{ + position:absolute; + background: image-url('icons/spinner.gif') no-repeat; + top: 50%; + left: 50%; + z-index: 1000; + height: 24px; + width: 24px; + opacity: 0.6; +} + +#sources { + display: none; +} + +.cartodb_infowindow.with_image_small { + + .protected-header .cover { + width:230px!important; + margin: 10px 0; + } + + h1 { + font-weight:bold; + font-size: 12px; + color: #FF5292; + line-height:normal; + text-transform: uppercase; + @extend .sans-serif; + + @include inline-block(); + white-space: nowrap; + width: 215px; + text-overflow: ellipsis; + overflow: hidden; + + } + + .top .infowindow_content label { padding-left: 0; } + + .top p { + display:block; + width:215px; + max-height:none; + padding:2px 0px; + margin: 2px 0 10px 0; + text-overflow:inherit; + overflow:auto; + white-space:normal; + + } + +} + +.cartodb_infowindow.with_image { + $width: 295px + 13; + + position:absolute; + left: -999px; + display:block; + width:$width; + max-height: 312px; + padding:0 0 6px; + text-align:center; + + a.close { + @include position(27px, 5px, false, false); + @include size(6px); + @include mixins-sprite(infowindow_close); + } + + $pR: 25px; + $pL: 17px; + + .outer_top { + width:$width; + padding:25px 0 5px 0; + background:url('icons/bkg_image_infowindow.png') no-repeat left top; + } + + .protected-header { + margin: 0 0 10px 6px; + width: 295px; + text-align: left; + + .analyse { + display: none; + @include position(65px, 5px, false, false); + @include size(41px, 42px); + @include mixins-sprite(graph); + + &.disabled { + @include opacity(.6); + @include pointer-events(none); + } + } + + .cover { + width: 295px; + height: 120px; + overflow:hidden; + clear:both; + + border-top: 1px solid rgba(#000, .2); + border-bottom: 1px solid rgba(#000, .2); + + } + + h1 { + padding: 0 15px; + margin: 0 0 17px 0; + font-weight:bold; + font-size: 12px; + color: #FF5292; + line-height:normal; + text-transform: uppercase; + @extend .sans-serif; + + @include inline-block(); + white-space: nowrap; + width: 245px; + text-overflow: ellipsis; + overflow: hidden; + + } + } + + .bottom { + @include position(false, false, -6px, -1px); + width:$width; + height:20px; + background:url('icons/bkg_image_infowindow.png') no-repeat right top; + } + + .shadow { + @include position(false, false, 6px, 6px); + @include size(294px, 24px); + background:url(backgrounds/white_shadow.png) repeat-x; + + z-index: 100; + } + + .top { + width:275px; + margin:auto; + + text-align: left; + + .jspTrack {background: none;} + .jspDrag {background: #EDEDED;} + .jspHover, .jspActive {background:#666666} + .jspVerticalBar { + margin-right: 3px; + } + + label { + display:block; + width:auto; padding:0 0 0 5px; + @extend .sans-serif; + font-size:11px; + color:#CCCCCC; + text-shadow:0 1px white; + } + + .infowindow_content { + width:100%; + padding: 0 0 10px 0; + max-height:113px; + overflow-y:auto; + overflow-x:hidden; + + /*margin: 0 0 0 5px;*/ + } + + p { + display:block; + width:255px; + max-height:20px; + padding:2px 0px; + margin:2px 0 10px 5px; + @extend .sans-serif; + font-weight:normal; + font-size:13px; + color:#666666; + border:none; + background:none; + text-shadow:0 1px white; + text-overflow:ellipsis; + overflow:hidden; + white-space:nowrap; + + &.empty {font-weight:normal; font-style:italic; color:#b7b7b7;} + } + + + .title { + + padding:2px 0 20px 0px; + margin:2px 0 10px 5px; + border-bottom: 3px solid #ccc; + + strong { + text-overflow:ellipsis; + display:block; + @extend .sans-serif; + font-weight:bold; + font-size:12px; + text-transform: uppercase; + } + } + + } +} +.cartodb_infowindow { + $width: 268px; + + position:absolute; + display:none; + width:$width; + max-height: 250px; + padding:0 0 6px; + text-align:center; + + + a.close { + @include position(20px, 5px, false, false); + @include size(6px); + @include mixins-sprite(infowindow_close); + } + + $pR: 25px; + $pL: 17px; + + .outer_top { + width:$width - $pL - $pR; + padding:25px $pR 5px $pL; + background:url('icons/bkg_infowindow.png') no-repeat left top; + } + + .bottom { + @include position(false, false, -6px, -1px); + width:$width; + height:20px; + background:url('icons/bkg_infowindow.png') no-repeat right top; + } + + .shadow { + @include position(false, false, 13px, 6px); + @include size(254px, 24px); + background:url(backgrounds/white_shadow.png) repeat-x; + + z-index: 100; + } + + .top { + width:245px; + height:200px; + margin:auto; + overflow-y:auto; + overflow-x:hidden; + + text-align: left; + + .jspTrack {background: none;} + .jspDrag {background: #EDEDED;} + .jspHover, .jspActive {background:#666666} + .jspVerticalBar { + margin-right: 3px; + } + + label { + display:block; + width:auto; padding:0 0 0 5px; + @extend .sans-serif; + font-size:11px; + color:#CCCCCC; + text-shadow:0 1px white; + } + + .infowindow_content { + width:100%; + padding: 0 0 10px 0; + + /*margin: 0 0 0 5px;*/ + } + + p { + display:block; + width:220px; + max-height:20px; + padding:2px 4px; + margin:2px 0 10px 0; + @extend .sans-serif; + font-weight:normal; + font-size:13px; + color:#666666; + border:none; + background:none; + text-shadow:0 1px white; + text-overflow:ellipsis; + overflow:hidden; + white-space:nowrap; + + &.empty {font-weight:normal; font-style:italic; color:#b7b7b7;} + } + + + .title { + + padding:2px 0 20px 0px; + margin:2px 0 10px 5px; + border-bottom: 3px solid #ccc; + + strong { + text-overflow:ellipsis; + display:block; + @extend .sans-serif; + font-weight:bold; + font-size:12px; + text-transform: uppercase; + } + } + + } +} + +.analysis_dropdown a { + text-decoration:none; +} + +.helper_bar { + display: none; + z-index: 1000; + + $width: 476px; + bottom: 39px; + + width: $width; + padding: 9px 13px; + + @include absolute-landscape-center($width); + @include border-radius(5px); + + background:#fff; + border: 1px solid #666666; + + font-family: "Helvetica Neue",Arial; + clear:both; + overflow:hidden; + + p { + float:left; + @include inline-block(); + margin: 10px 0 0 0; + padding: 0; + color: #666666; + font-size:13px; + } + + .options { float:right; } + + a { + @include inline-block(); + width: 70px; + padding: 10px 0; + border: #666666; + @include border-radius(3px); + font-weight:bold; + + text-align: center; + font-size:11px; + text-transform:uppercase; + text-decoration:none; + + &.done { + background:#A1BA42; + color: #fff; + border: 1px solid #839C26; + + &:hover { + background: darken(#A1BA42, 10%); + border-color:darken(#A1BA42, 10%); + } + + &.disabled { + opacity: .5; + + &:hover { + background:#A1BA42; + border-color: #839C26; + cursor: default; + } + } + } + + &.cancel { + background: #fff; + border: 1px solid #CCCCCC; + color: #666666; + + &:hover { background:#f1f1f1; } + + } + } +} + +.analysis_reset { + display: none; + position: absolute; + z-index: 1000; + top: 0; + left: 0; + width: 170px; + margin-left: -80px; + margin-top: -105px; + font-size: 13px; + font-family: "Helvetica Neue",Arial; + text-align: center; + padding: 10px 10px 15px; + color: #ccc; + @include background(linear-gradient(top, rgba(#000, .8), rgba(#000, .9) 100%)); + @include border-radius(3px); + + p { margin-bottom: 10px; } + + span.tail { + position:absolute; + bottom: -8px; + left: 50%; + width: 0; + height: 0; + margin: 0 0 0 -8px; + border-left: 8px solid transparent; + border-right: 8px solid transparent; + border-top: 8px solid #000; + } + + .button { + display: inline-block; + line-height: 30px; + padding: 0 10px; + color: #eee; + text-decoration: none; + text-transform: uppercase; + font-size: 12px; + font-weight: 500; + background: #181818; + @include border-radius(5px); + + &:hover { + color: #fff; + } + } + + .delete { + border: 1px solid #600; + background: #B20000; + } +} + +.qtip-default { + border: 1px solid #ccc; + background: #fff; + @include border-radius(3px); + @include box-shadow(0 0 3px rgba(#000, .2)); +} + +.qtip-content { + padding: 0; + + li { + &:first-child a { + border-top: 0; + } + + a { + display: block; + border-top: 1px solid #ddd; + padding: 10px; + font-size: 13px; + @extend .serif; + color: #666; + + &:hover { + background: #fafafa; + text-decoration: underline; + } + } + } +} + +label { + display: block; + @extend .sans-serif; + color: #333333; + font-weight:bold; + font-size:12px; + text-decoration:none; + text-transform:uppercase; +} + + +.input_title { + + &.short { width: 285px; } + + &.error label, .field_with_errors label { color: #C12000; } + + width: 590px; + clear:both; + overflow:hidden; + + @extend .sans-serif; + color: #C12000; + font-size:13px; + + /*background:blue;*/ + + label { float: left; } + + .error-message { + display:block; + float: right; + } +} + +.input-field { + + @include inline-block(); + height:36px; + width: 416px; + + background: url(backgrounds/bkg_form_input.png) no-repeat left 0; + + input[type="text"], input[type="password"], input[type="email"],input[type="date"] { + width:100%; + height:37px; + margin: 0 0 0 7px; + padding: 0 7px 0 5px; + + font-size: 14px; + + background:url(backgrounds/bkg_form_input.png) repeat-x right -36px; + border:none; + outline:none; + } + + &.short { + width: 267px; + height: 42px; + background:url(backgrounds/bkg_form_input_big.png) no-repeat left 0; + + input[type="text"], + input[type="date"] { + height:43px; + background:url(backgrounds/bkg_form_input_big.png) repeat-x right -42px; + } + + &.error { + background-position: left -84px; + input[type="text"] { background-position: right -126px; } + } + } + + &.big { + height: 42px; + background:url(backgrounds/bkg_form_input_big.png) no-repeat left 0; + + input[type="text"] { + height:43px; + background:url(backgrounds/bkg_form_input_big.png) repeat-x right -42px; + } + } + + &.huge { + width: 570px; + height: 42px; + background:url(backgrounds/bkg_form_input_big.png) no-repeat left 0; + + input[type="password"], input[type="text"], input[type="email"] { + height:43px; + background:url(backgrounds/bkg_form_input_big.png) repeat-x right -42px; + } + + &.error { + background-position: left -84px; + input[type="text"], input[type="password"], input[type="email"] { background-position: right -126px; } + } + + } + + &.textarea { + + $width: 573px; + $height: 196px; + + @include inline-block(); + @include size($width, $height); + + background:url(backgrounds/bkg_form_textarea.png) no-repeat left 0; + + textarea { + @include size(100%, $height - 7*2); + margin: 0 0 0 5px; + padding: 7px 5px; + + font-size: 14px; + background:url(backgrounds/bkg_form_textarea.png) repeat-x right -1*$height; + + border:none; + outline:none; + resize: none; + } + } + +} + +.jspHorizontalBar { + display: none; +} + +label { + display: block; + @extend .sans-serif; + color: #333333; + font-weight:bold; + font-size:12px; + text-decoration:none; + text-transform:uppercase; +} + +.field a.checkbox { + + @include size(17px, 14px); + @include inline-block(); + padding: 0 0 0 20px; + + @include mixins-sprite(checkbox, $offset-y: 2px); + + font-size: 15px; + color: #666666; + font-weight:normal; + display: inline; + + text-transform: none; + + @extend .sans-serif; + + &.hover { + @include mixins-sprite(checkbox_hover, $offset-y: 2px); + } + + &.checked { + @include mixins-sprite(checkbox_checked, $offset-y: 2px); + } + +} + +.input_title { + + &.short { width: 285px; } + + &.error label, .field_with_errors label { color: #C12000; } + + width: 590px; + clear:both; + overflow:hidden; + + @extend .sans-serif; + color: #C12000; + font-size:13px; + + /*background:blue;*/ + + label { float: left; } + + .error-message { + display:block; + float: right; + } +} + +.input-field { + + @include inline-block(); + height:36px; + width: 416px; + + background: url(backgrounds/bkg_form_input.png) no-repeat left 0; + + input[type="text"], input[type="password"], input[type="email"] { + width:100%; + height:37px; + margin: 0 0 0 7px; + padding: 0 7px 0 5px; + + font-size: 14px; + + background:url(backgrounds/bkg_form_input.png) repeat-x right -36px; + border:none; + outline:none; + } + + &.short { + width: 267px; + height: 42px; + background:url(backgrounds/bkg_form_input_big.png) no-repeat left 0; + + input[type="text"] { + height:43px; + background:url(backgrounds/bkg_form_input_big.png) repeat-x right -42px; + } + + &.error { + background-position: left -84px; + input[type="text"] { background-position: right -126px; } + } + } + + &.big { + height: 42px; + background:url(backgrounds/bkg_form_input_big.png) no-repeat left 0; + + input[type="text"] { + height:43px; + background:url(backgrounds/bkg_form_input_big.png) repeat-x right -42px; + } + } + + &.huge { + width: 570px; + height: 42px; + background:url(backgrounds/bkg_form_input_big.png) no-repeat left 0; + + input[type="password"], input[type="text"], input[type="email"] { + height:43px; + background:url(backgrounds/bkg_form_input_big.png) repeat-x right -42px; + } + + &.error { + background-position: left -84px; + input[type="text"], input[type="password"], input[type="email"] { background-position: right -126px; } + } + + } + + &.textarea { + + $width: 573px; + $height: 196px; + + @include inline-block(); + @include size($width, $height); + + background:url(backgrounds/bkg_form_textarea.png) no-repeat left 0; + + textarea { + @include size(100%, $height - 7*2); + margin: 0 0 0 5px; + padding: 7px 5px; + + font-size: 14px; + background:url(backgrounds/bkg_form_textarea.png) repeat-x right -1*$height; + + border:none; + outline:none; + resize: none; + } + } + +} + +#fileupload { display: none; } + +body.stories { + + header { + height: 205px; + border-bottom: 1px solid $cGray4; + z-index: 1000; + } + + .btn.edit { + position:absolute; + right:0; + top: 95px; + } + + .btn.destroy_story, + .btn.destroy { + position:absolute; + right:0; + top: 95px; + } + + &.index { + hgroup { width: 927px; text-align: center; } + + header { + height: 199px; + } + + hgroup h1 { top: 27px; } + + hgroup h1 { + font-size:51px; + font-family: Georgia; + } + + } + + &.show { + hgroup { width: 927px; text-align: center; } + + hgroup h1 { top: 20px; } + + hgroup h1 a { + color: #A0B941; + font-size:31px; + font-family: Georgia; + } + + } + + #content { + + background: $cWhite; + + .inner { + position:relative; + width: 927px; + margin: auto; + } + } + + .featured { + padding: 60px; + + li { + margin-bottom: 20px; + word-spacing: -1em; + position:relative; + height: 174px; + padding: 50px 0; + + img { + position:absolute; + + top: 5px; + left: 5px; + + width: 276px; + height: 276px; + @include border-radius(300px); + } + + &.left .frame { left: 0px; } + &.right .frame { right: 0px; } + + .frame { + + display:block; + position:absolute; + top: 0px; + width: 286px; + height: 286px; + @include border-radius(300px); + background:#EBEBEB; + z-index: 10; + word-spacing: normal; + + .coords { + @include absolute-center(175px, 83px); + background: url(backgrounds/bkg_tooltip.png) no-repeat; + text-align: center; + z-index: 100; + + strong { + position:relative; + top: 30px; + color: #9EB741; + font-size: 12px; + @extend .sans-serif; + } + } + } + + .content { + @include inline-block(); + width: 600px; + word-spacing: normal; + + h2 a { + margin: 0 0 12px 0; + @extend .serif; + font-size:29px; + text-transform: none; + font-weight: normal; + line-height:normal; + color: #333333; + + &:hover { + text-decoration: underline; + } + } + + p { + @extend .sans-serif; + font-size: 16px; + line-height: 20px; + color: $cGray2; + } + + a { + display: block; + margin: 10px 0; + @extend .sans-serif; + font-size: 12px; + font-weight: bold; + color: $cGreen1; + text-transform: uppercase; + text-decoration: none; + } + } + + img, div { vertical-align: middle; } + + } + + .right { + text-align: left; + .content { text-align: right; } + + } + + .left { + text-align: right; + .content { text-align:left; } + } + + .pagination { + margin: 60px 0 0 0; + padding: 30px 0 0 0; + border-top: 1px solid #E5E5E5; + text-align: center; + } + } + + .more_stories { + + background-color: $cGray5; + padding: 45px 0; + text-align: center; + + .inner { + + h1 { + margin: 0 0 12px 0; + @extend .serif; + font-size: 36px; + color: white; + } + + p { + @extend .sans-serif; + font-size: 15px; + font-weight:400; + line-height: 1.2em; + + color: $cGray6; + + a { + font-weight: bold; + text-decoration: none; + color: $cLime; + } + } + + ul { + padding: 40px; + + li { + @include inline-block(); + @include size(117px); + + position:relative; + margin: 0 20px; + + .infowindow { + padding: 12px; + @include position(false, false, 125px, false); + width: 156px; + background:red; + @include border-radius(3px); + + background:#111; + + z-index: 1000; + + color: #999999; + font-size: 12px; + + @extend .sans-serif; + + text-align: left; + + .tip { + @include size(13px, 6px); + @include absolute-landscape-center(13px); + @include mixins-sprite(infowindow_tip); + bottom: -6px; + } + + strong a { + + display:block; + margin: 0 0 5px 0; + width: 140px; + text-align: left; + + color: #F3F2F0; + font-family: Georgia, serif; + font-size: 17px; + font-weight:normal; + line-height:normal; + + text-decoration :none; + + &:hover { text-decoration: underline; } + } + + } + + .frame { + @include position(0, false, false, 0); + @include size(100%); + @include border-radius(300px); + + background:#656565; + z-index: 10; + } + + img { + @include absolute-center(105px); + @include border-radius(120px); + } + + .exclamation { + @include absolute-center(105px); + @include mixins-sprite(gfw_default_story_map_icon); + z-index: 10; + } + } + } + + hr { + border: none; + width: 909px; + height: 1px; + margin: 0 0 40px 0; + background-color: $cGray8; + } + + .submit { + @include inline-block(); + margin-top: 45px; + padding: 10px 20px; + background-color: $cGreen1; + @extend .sans-serif; + font-size: 11px; + font-weight: bold; + color: white; + text-decoration: none; + text-transform: uppercase; + } + + } + + } + + .story_form { + position: relative; + margin-top: 50px; + + .inner { + position: relative; + padding: 50px 0; + } + + .title { + padding: 50px 0; + border-bottom: 1px solid #CCCCCC; + + h2 { + @extend .inner; + @extend .font-big; + padding: 0; + font-size: 49px; + color: black; + text-align: left; + } + } + + .input-field { + margin: 5px 0; + vertical-align: top; + } + + .field { + margin: 0 0 25px; + + &.last { + margin: 0 0 50px; + } + + /*.field_title { + clear:both; + overflow:hidden; + background:red; + color: red; + label { + float:left; + + } + + .field_with_errors { + @include inline-block(); + } + + + .error-message { + float:right; + } + + + }*/ + +&.media { + clear:both; + overflow:hidden; + + .inner_media { + @include inline-block(); + width: 588px ; + margin: 5px 0; + } + + li { + @include inline-block(); + } + + .upload_picture { + float:left; + + @include inline-block(); + @include size(132px, 78px); + margin: 0 10px 10px 0; + background: transparent url('buttons/bkg_button_add_media.png') no-repeat center center; + + &:hover { background: transparent url('buttons/bkg_button_add_media_hover.png') no-repeat center center; } + + } + + .thumbnails { + + float:left; + width: 585px; + + .destroy { + @include size(18px); + @include position(-9px, -9px, false, false); + } + + .thumbnail { + position:relative; + @include inline-block(); + @include size(132px, 76px); + margin: 0 7px 10px 0; + border: 2px solid $cWhite; + background:#fff; + cursor: move; + + .inner_box { + @include size(132px, 76px); + clear:both; + overflow: hidden; + } + + &:hover { border: 2px solid #9EB741; } + + &.preview { + cursor: default; + border: 1px solid #9EB741; + + &:hover { border: 1px solid #9EB741; } + + } + + img { + width:100%; + } + + .filename { + text-align: center; + width: 100%; + @include position(false, false, 12px, 0); + @extend .sans-serif; + font-size: 13px; + color: $cGray2; + } + .spinner { @include position(30px, false, false, 65px); } + + } + + } + + .hint { + margin-left: 50px; + } +} + +&.affected-area { + + label, .map-help { + @include inline-block(); + margin: 0 0 7px 0; + } + + .map-help { + @extend .sans-serif; + font-size: 13px; + color: $cGray2; + margin-left: 5px; + } + +} + +.stories_map-container { + @include size(588px, 293px); + position:relative; + background-color:black; + margin-top: 5px; + + .remove { + display:none; + @include position(5px, 5px, false, false); + padding: 7px 6px; + + background: #fff; + + border: 1px solid #333; + text-transform: uppercase; + color: #999999; + font-size: 11px; + font-weight:bold; + text-decoration: none; + @extend .sans-serif; + + z-index:1000; + } + + .map { + height: 293px; + } +} + + +.hint { + @include inline-block(); + width: 284px; + margin-left: 372px; + @extend .sans-serif; + font-size: 15px; + color: $cGray2; + line-height: 1.2em; + vertical-align: top; +} + +.optional { + width: 588px; + + label, span { @include inline-block(); } + + span { + float: right; + @extend .sans-serif; + font-size: 13px; + color: $cGray2; + } + + &.short { + width: 284px; + } + +} + +input[type=submit] { + display:block; + background: transparent url('buttons/bkg_button_save_story.png') no-repeat center center; + border: 0 none; + width: 156px; + height: 40px; + @extend .sans-serif; + font-weight: bold; + color: white; + text-transform: uppercase; + text-decoration: none; + text-align: center; + + &:hover { @include dark-glow(0.2); } + + &.disabled { background:#ccc; } +} + +} + +aside { + position: absolute; + right: 0; + top: 0; + width: 285px; + + h3 { + padding: 0 0 20px; + margin-bottom: 20px; + border-bottom: 1px solid $cGray4; + @extend .sans-serif; + font-weight: bold; + font-size:13px; + color: $cGray1; + text-transform: uppercase; + } + + + p, + ul { + @extend .sans-serif; + font-size: 15px; + color: $cGray2; + line-height: 1.2em; + padding-bottom: 20px; + } + + ul { + list-style: disc; + margin-left: 20px; + } + + li { margin-bottom: 10px; } +} + + + } + + .story { + + .title { + background:transparent; + border-bottom: 1px solid $cGray4; + margin-top: -168px; + + h2.compact { + padding: 17px 0 34px 0; + + a { + @extend .inner; + @extend .serif; + padding: 0; + color: $cGreen1; + font-size: 31px; + text-align:center; + text-decoration: none; + } + } + + } + + .inner { + padding: 50px 0; + + .header { + background:transparent; + h1 { + @extend .serif; + font-size: 51px; + text-align: center; + } + + .when-and-who { + @extend .sans-serif; + font-size: 15px; + color: $cGray3; + text-align:center; + margin-top: 10px; + margin-bottom: 40px; + } + } + + .carrousel { + position: relative; + margin: 0 0 40px 0; + + $width: 918px; + $height: 362px; + + @include size($width, $height); + + .frame { + + display:block; + position:absolute; + bottom: 35px; + right: 35px; + width: 286px; + height: 286px; + @include border-radius(300px); + background:#333; + background:rgba(#000, .5); + z-index: 100; + word-spacing: normal; + + img { + position:absolute; + + top: 5px; + left: 5px; + + width: 276px; + height: 276px; + @include border-radius(300px); + } + + + .coords { + @include absolute-center(175px, 83px); + background: url(backgrounds/bkg_tooltip.png) no-repeat; + text-align: center; + z-index: 100; + + strong { + position:relative; + top: 30px; + color: #9EB741; + font-size: 12px; + @extend .sans-serif; + } + } + } + + + + .previous, .next { + position:absolute; + display:block; + width:68px; + height:68px; + } + + .previous { + top: 30px; + left: -34px; + @include mixins-sprite(gfw_default_story_map_icon); + z-index: 100; + } + + .next { + bottom: 30px; + right: -25px; + @include mixins-sprite(button_story_gallery_right); + z-index: 100; + } + + li { + display:none; + @include position(0, false, false, 0); + z-index: 10; + + &, img { + @include size($width, $height); + } + + } + + } + + .details { + p { + @extend .sans-serif; + font-size: 16px; + line-height: 20px; + color: $cGray2; + margin-bottom: 10px; + + -moz-column-count: 2; + -moz-column-gap: 20px; + -o-column-count: 2; + -o-column-gap: 20px; + -webkit-column-count: 2; + -webkit-column-gap: 20px; + column-count: 2; + column-gap: 20px; + + .dropcap{ + @extend .serif; + float:left; + font-size:400%; + margin-top: 16px; + margin-right: 10px; + margin-bottom: 20px; + } + + } + + } + + .share_buttons { + margin: 15px 0 0 0; + + iframe.twitter-share-button { + @include inline-block(); + } + + div.fb-like { + @include inline-block(); + margin: 0 10px 0 0; + } + + } + + } + + } + + } + +.searchbox { + display: none; + width: 195px; + position: absolute; + height: 20px; + top: 35px; + right: 237px; + @include border-radius(3px); + @include box-shadow(0px 0px 3px #666); + background: #FFF; + border: 1px solid #666; + cursor: move; + z-index: 1000; + padding: 10px; + + strong { + font: bold 11px "Helvetica Neue", Helvetica, Arial, sans-serif; + text-transform: uppercase; + font-size: 11px; + color: #ccc; + float: left; + margin-right: 10px; + line-height: 22px; + } + + input { + float: left; + border: none; + font-size: 12px; + margin: 3px 0 0; + padding: 0; + width: 122px; + + &:focus { + outline: none; + } + } + + button { + float: left; + height: 16px; + width: 16px; + background: url('buttons/search.png') no-repeat center center; + border: 0; + } +} diff --git a/app/assets/stylesheets/countries.scss b/app/assets/stylesheets/modules/countries.scss similarity index 98% rename from app/assets/stylesheets/countries.scss rename to app/assets/stylesheets/modules/countries.scss index 84a63cc568..6488ff32c5 100644 --- a/app/assets/stylesheets/countries.scss +++ b/app/assets/stylesheets/modules/countries.scss @@ -1,17 +1,3 @@ -@import 'compass/css3/box-shadow'; -@import 'compass/css3/border-radius'; -@import 'compass/css3/opacity'; -@import 'compass/css3/background-size'; -@import 'compass/css3/images'; -@import 'compass/css3/text-shadow'; -@import 'compass/css3/inline-block'; - -@import 'fonts'; -@import 'helpers'; - -@import 'country-icons/*.png'; -/**/ - .countries { &.index .content { padding: 15px 0 56px; diff --git a/app/assets/stylesheets/home.scss b/app/assets/stylesheets/modules/home.scss similarity index 98% rename from app/assets/stylesheets/home.scss rename to app/assets/stylesheets/modules/home.scss index 79f59fa3f2..2e02d04656 100644 --- a/app/assets/stylesheets/home.scss +++ b/app/assets/stylesheets/modules/home.scss @@ -1,24 +1,3 @@ -//= require tipsy - -//= require mixins -//= require map - - -@import 'compass/css3/border-radius'; -@import 'compass/css3/box-shadow'; -@import 'compass/css3/user-interface'; -@import 'compass/css3/background-size'; -@import 'compass/css3/opacity'; -@import 'compass/css3/inline-block'; -@import 'compass/css3/text-shadow'; -@import 'compass/css3/transform'; - -@import 'fonts'; -@import 'helpers'; - -@import 'home-icons/*.png'; - - /* =Globals ----------------------------------------------- */ diff --git a/app/assets/stylesheets/static.scss b/app/assets/stylesheets/modules/static.scss similarity index 97% rename from app/assets/stylesheets/static.scss rename to app/assets/stylesheets/modules/static.scss index 6ca4c68012..273e06b0e7 100644 --- a/app/assets/stylesheets/static.scss +++ b/app/assets/stylesheets/modules/static.scss @@ -1,17 +1,3 @@ -@import 'compass/css3/border-radius'; -@import 'compass/css3/opacity'; -@import 'compass/css3/box-shadow'; -@import 'compass/css3/inline-block'; - -@import 'fonts'; -@import 'helpers'; - -@import 'static-icons/*.png'; -/**/ -@import 'static-logos/*.png'; -/**/ - - .static-inner { @extend .clearfix; background: #F2F2F3; diff --git a/app/assets/stylesheets/static_public.css b/app/assets/stylesheets/modules/static_public.css similarity index 100% rename from app/assets/stylesheets/static_public.css rename to app/assets/stylesheets/modules/static_public.css diff --git a/app/assets/stylesheets/stories.scss b/app/assets/stylesheets/modules/stories.scss similarity index 96% rename from app/assets/stylesheets/stories.scss rename to app/assets/stylesheets/modules/stories.scss index 2d5d4acd17..d5492cab16 100644 --- a/app/assets/stylesheets/stories.scss +++ b/app/assets/stylesheets/modules/stories.scss @@ -1,16 +1,3 @@ -@import 'compass/css3/border-radius'; -@import 'compass/css3/box-shadow'; -@import 'compass/css3/columns'; -@import 'compass/css3/inline-block'; - -@import 'fonts'; -@import 'helpers'; -@import 'forms'; - -@import 'stories-icons/*.png'; -/**/ - - .stories { .header { height: 205px; diff --git a/app/assets/templates/map/analysis.html b/app/assets/templates/analysis.html similarity index 100% rename from app/assets/templates/map/analysis.html rename to app/assets/templates/analysis.html diff --git a/app/assets/templates/map/circle.html b/app/assets/templates/circle.html similarity index 100% rename from app/assets/templates/map/circle.html rename to app/assets/templates/circle.html diff --git a/app/assets/templates/map/filter.html b/app/assets/templates/filter.html similarity index 100% rename from app/assets/templates/map/filter.html rename to app/assets/templates/filter.html diff --git a/app/assets/templates/map/legend.html b/app/assets/templates/legend.html similarity index 100% rename from app/assets/templates/map/legend.html rename to app/assets/templates/legend.html diff --git a/app/assets/templates/map/search.html b/app/assets/templates/search.html similarity index 100% rename from app/assets/templates/map/search.html rename to app/assets/templates/search.html diff --git a/app/assets/templates/map/share.html b/app/assets/templates/share.html similarity index 100% rename from app/assets/templates/map/share.html rename to app/assets/templates/share.html diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb index fd02f847b4..920c2ad1d5 100644 --- a/app/views/layouts/application.html.erb +++ b/app/views/layouts/application.html.erb @@ -22,7 +22,6 @@ <%= favicon_link_tag 'favicon.ico' %> <%= stylesheet_link_tag "application", :media => "all" %> - <%= controller_stylesheet_link_tag %> <%= javascript_include_tag "modernizr-2.6.2.min" %> <%= csrf_meta_tags %> diff --git a/app/views/layouts/map.html.erb b/app/views/layouts/map.html.erb index ada32e0f1d..874df13c3f 100644 --- a/app/views/layouts/map.html.erb +++ b/app/views/layouts/map.html.erb @@ -20,7 +20,6 @@ <%= favicon_link_tag 'favicon.ico' %> <%= stylesheet_link_tag "application", :media => "all" %> - <%= controller_stylesheet_link_tag %> <%= javascript_include_tag "modernizr-2.6.2.min" %> <%= csrf_meta_tags %> From c1961cefa4948f4dc69c8420d93c5f435614bd0a Mon Sep 17 00:00:00 2001 From: David Gonzalez Date: Thu, 19 Jun 2014 10:14:51 -0700 Subject: [PATCH 105/823] remove poi geometries & landscape from grayscale basemap --- lib/assets/javascripts/gfw/helpers.js.erb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/assets/javascripts/gfw/helpers.js.erb b/lib/assets/javascripts/gfw/helpers.js.erb index 74ff0a9af1..fa246a2f84 100644 --- a/lib/assets/javascripts/gfw/helpers.js.erb +++ b/lib/assets/javascripts/gfw/helpers.js.erb @@ -55,7 +55,7 @@ config.MAPSTYLES = {}; config.MAPSTYLES.grayscale = { type: 'style', - style: [ { "featureType": "water" }, { "featureType": "transit", "stylers": [ { "saturation": -100 } ] }, { "featureType": "road", "stylers": [ { "saturation": -100 } ] }, { "featureType": "poi", "stylers": [ { "saturation": -100 } ] }, { "featureType": "landscape", "stylers": [ { "saturation": -100 } ] }, { "featureType": "administrative", "stylers": [ { "saturation": -100 } ] },{ "featureType": "poi.park", "elementType": "geometry", "stylers": [ { "visibility": 'off' } ] } ] + style: [ { "featureType": "water" }, { "featureType": "transit", "stylers": [ { "saturation": -100 } ] }, { "featureType": "road", "stylers": [ { "saturation": -100 } ] }, { "featureType": "poi", "stylers": [ { "saturation": -100 } ] }, { "featureType": "landscape", "stylers": [ { "saturation": -100 },{ "lightness": 90 } ] }, { "featureType": "administrative", "stylers": [ { "saturation": -100 } ] },{ "featureType": "poi", "elementType": "geometry", "stylers": [ { "visibility": 'off' } ] } ] } config.MAPSTYLES.terrain = { From dcc8dbb006763fad7c5b9663ed780caca865b4be Mon Sep 17 00:00:00 2001 From: Aaron Date: Thu, 19 Jun 2014 12:24:05 -0700 Subject: [PATCH 106/823] add more tests to anlysis_spec --- app/assets/javascripts/map/analysis.js | 14 +- app/assets/javascripts/map/app.js | 10 ++ app/assets/javascripts/map/nsa.js | 15 +- jstest/helpers/api_responses.js | 8 +- jstest/spec-runner.js | 1 + jstest/spec/analysis_spec.js | 210 +++++++++++++++++++++---- 6 files changed, 221 insertions(+), 37 deletions(-) diff --git a/app/assets/javascripts/map/analysis.js b/app/assets/javascripts/map/analysis.js index e0206aba0e..0f0cd53e86 100644 --- a/app/assets/javascripts/map/analysis.js +++ b/app/assets/javascripts/map/analysis.js @@ -1,3 +1,13 @@ +/** + * The analysis module. + * + * To get analysis results from this module, first subscribe to the + * 'analysis/get-results' topic + * To get analysis results, publish the 'analysis/get' event and pass in a + * config object with analysis parameters: + * + * + */ define([ 'jquery', 'underscore', @@ -92,10 +102,10 @@ define([ url, {}, function(response) { - mps.publish('analysis/get-results', [response]); + mps.publish('analysis/get-success', [response]); }, function(responseText, status, error) { - mps.publish('analysis/get-results-error', [responseText, status, error]); + mps.publish('analysis/get-failure', [responseText, status, error]); }); } }); diff --git a/app/assets/javascripts/map/app.js b/app/assets/javascripts/map/app.js index 62f5401fdb..7bd26d5718 100644 --- a/app/assets/javascripts/map/app.js +++ b/app/assets/javascripts/map/app.js @@ -27,6 +27,16 @@ define([ }; } + /** + * Returns true if string contains substring. + */ + if (!String.prototype.contains) { + String.prototype.contains = function(substr) { + var args = arguments; + return this.indexOf(substr) > -1; + }; + } + _.mixin({ capitalize: function(string) { return string.charAt(0).toUpperCase() + string.substring(1).toLowerCase(); diff --git a/app/assets/javascripts/map/nsa.js b/app/assets/javascripts/map/nsa.js index 1a8ae84891..d2fb5cf8aa 100644 --- a/app/assets/javascripts/map/nsa.js +++ b/app/assets/javascripts/map/nsa.js @@ -1,3 +1,7 @@ +/** + * Module for executing async HTTP requests. + * + */ define([ 'jquery', 'mps' @@ -5,12 +9,13 @@ define([ return { /** - * Executes an RPC asynchronously. + * Async HTTP request to supplied URL and optional data. * - * Args: - * url: The URL endpoint - * data: Object with parameters. - * callback: Object with a success and error function. + * @param {string} url The URL. + * @param {object} data The URL query parameters. + * @param {function} successCb The success callback function. + * @param {function} errorCb The error callback function + * @return {object} The jqXHR object handle. */ spy: function(url, data, successCb, errorCb) { var jqxhr = null; diff --git a/jstest/helpers/api_responses.js b/jstest/helpers/api_responses.js index 6cf1c48195..a97d3d25b2 100644 --- a/jstest/helpers/api_responses.js +++ b/jstest/helpers/api_responses.js @@ -3,11 +3,15 @@ var ApiResponse = { iso: { success: { status: 200, - responseText: '{"coverage": "Humid tropical forest biome", "description": "Alerts where forest disturbances have likely occurred.", "id": "forma-alerts", "iso": "bra", "name": "FORMA", "resolution": "500 x 500 meters", "source": "MODIS", "timescale": "January 2006 to present", "units": "Alerts", "updates": "16 day", "value": 1088866}' + responseText: '{"data": {}}' }, notfound: { status: 404, - responseText: '404 Not Found

      404 Not Found

      The resource could not be found.

      ' + responseText: '404' + }, + failure: { + status: 400, + responseText: '{"error": {}}' } }, wdpa: { diff --git a/jstest/spec-runner.js b/jstest/spec-runner.js index 96bc053133..a87af6e5c5 100644 --- a/jstest/spec-runner.js +++ b/jstest/spec-runner.js @@ -26,6 +26,7 @@ require.config({ views: ['app/assets/javascripts/map/views'], models: ['app/assets/javascripts/map/models'], collections: ['app/assets/javascripts/map/collections'], + itertools: ['vendor/assets/javascripts/itertools'], spec: ['../jstest/spec'], helpers: ['../jstest/helpers'], diff --git a/jstest/spec/analysis_spec.js b/jstest/spec/analysis_spec.js index be7f3738fd..2119e8a6dd 100644 --- a/jstest/spec/analysis_spec.js +++ b/jstest/spec/analysis_spec.js @@ -3,9 +3,93 @@ define([ 'analysis', 'mps', 'underscore', - 'helpers/api_responses' + 'helpers/api_responses', ], function(app, analysis, mps, _) { + var k_combinations = function(set, k) { + var i, j, combs, head, tailcombs; + + if (k > set.length || k <= 0) { + return []; + } + + if (k == set.length) { + return [set]; + } + + if (k == 1) { + combs = []; + for (i = 0; i < set.length; i++) { + combs.push([set[i]]); + } + return combs; + } + + combs = []; + for (i = 0; i < set.length - k + 1; i++) { + head = set.slice(i, i+1); + tailcombs = k_combinations(set.slice(i + 1), k - 1); + for (j = 0; j < tailcombs.length; j++) { + combs.push(head.concat(tailcombs[j])); + } + } + return combs; + }; + + var combinations = function(set) { + var k, i, combs, k_combs; + combs = []; + + // Calculate all non-empty k-combinations + for (k = 1; k <= set.length; k++) { + k_combs = k_combinations(set, k); + for (i = 0; i < k_combs.length; i++) { + combs.push(k_combs[i]); + } + } + return combs; + }; + + /** + * Reduce supplied array of param arrays into a single object. + * + * > var params = [['a', 1], ['b', 2]]; + * > reduce_params(params); + * {a: 1, b: 2} + * + * @param {array} array of param arrays (e.g., [['a', 1], ['b', 2]]) + * @return {object} params as object (e.g., {a: 1, b:2}) + */ + var reduce_params = function(combo) { + return _.reduceRight( + combo, + function(memo, pair) { + memo[pair[0]] = pair[1]; + return memo; + }, + {}); + }; + + /** + * Return array of objects for all combinations of supplied params array. + * + * > get_config_combos([['a', 1], ['b', 2], ['c', 3]]); + * [ { a: 1 }, + * { b: 2 }, + * { c: 3 }, + * { b: 2, a: 1 }, + * { c: 3, a: 1 }, + * { c: 3, b: 2 }, + * { c: 3, b: 2, a: 1 } ] + * + * @param {array} params array of params [key, value] + * @return {array} array of param objects + */ + var param_combos = function(params) { + var combos = combinations(params); + return _.map(combos, reduce_params); + }; + describe("The analysis module", function() { var apis = { global: "http://{0}/forest-change/{1}{?period,geojson,download,bust,dev}", @@ -27,22 +111,48 @@ define([ }); }); + describe("Analyze forest change layers", function() { var request = null; var originalTimeout; beforeEach(function() { jasmine.Ajax.install(); - originalTimeout = jasmine.DEFAULT_TIMEOUT_INTERVAL; - jasmine.DEFAULT_TIMEOUT_INTERVAL = 10000; }); - afterEach(function() { - jasmine.DEFAULT_TIMEOUT_INTERVAL = originalTimeout; + + describe("FORMA Alerts by country - Test API URLs", function() { + var params = [ + ['bust', 1], + ['period', '2008-01-01,2009-01-01']]; + var combos = param_combos(params); + + it("creates correct URLs via get_url()", function() { + + _.each(combos, function(combo) { + var url = null; + var path = 'forest-change/forma-alerts/admin/bra'; + var kvp = null; + + combo.layerName = 'forma-alerts'; + combo.iso = 'bra'; + url = analysis.get_url(combo); + expect(url.contains(path)).toBe(true); + for (var key in combo) { + if (key === 'iso' || key === 'layerName') { + continue; + } + kvp = '{0}={1}'.format(key, encodeURIComponent(combo[key])); + console.log(kvp, url); + expect(url.contains(kvp)).toBe(true); + } + }); + }); + }); - describe("FORMA Alerts by country - success", function() { - var callback = null; + describe("FORMA Alerts by country - Test Success", function() { + var cb = null; var url = 'http://beta.gfw-apis.appspot.com/forest-change/forma-alerts/admin/bra?period=2001%2C2008'; beforeEach(function(done) { @@ -55,30 +165,30 @@ define([ expect(request.method).toBe('POST'); expect(request.data()).toEqual({}); - callback = { + cb = { spy: function(response) { done(); } }; - spyOn(callback, 'spy').and.callThrough(); - mps.subscribe('analysis/get-results', callback.spy); + spyOn(cb, 'spy').and.callThrough(); + mps.subscribe('analysis/get-success', cb.spy); request.response(ApiResponse.forma_alerts.iso.success); }); - it("The analysis/get-results callback called with correct API response", function() { - var response = null; - var expected = JSON.parse(ApiResponse.forma_alerts.iso.success.responseText); - - expect(callback.spy).toHaveBeenCalled(); - response = callback.spy.calls.mostRecent().args[0]; - expect(response).toEqual(expected); + it("The analysis/get-results cb called with correct API response", function() { + var cbArgs = cb.spy.calls.mostRecent().args; + var text = cbArgs[0]; + var expectedText = ApiResponse.forma_alerts.iso.success.responseText; + + expect(cb.spy).toHaveBeenCalled(); + expect(text).toEqual(JSON.parse(expectedText)); }); }); - describe("FORMA Alerts by country - failure", function() { - var callback = null; + describe("FORMA Alerts by country - Not found", function() { + var cb = null; var url = 'http://beta.gfw-apis.appspot.com/forest-change/forma-alerts-foo/admin/bra?period=2001%2C2008'; beforeEach(function(done) { @@ -91,24 +201,68 @@ define([ expect(request.method).toBe('POST'); expect(request.data()).toEqual({}); - callback = { + cb = { spy: function(responseText, status, error) { done(); } }; - spyOn(callback, 'spy').and.callThrough(); - mps.subscribe('analysis/get-results-error', callback.spy); + spyOn(cb, 'spy').and.callThrough(); + mps.subscribe('analysis/get-failure', cb.spy); request.response(ApiResponse.forma_alerts.iso.notfound); }); - it("The analysis/get-results callback called with correct API response", function() { - var response = null; - var expected = ApiResponse.forma_alerts.iso.notfound.responseText; + it("The analysis/get-results cb called with correct API response", function() { + var cbArgs = cb.spy.calls.mostRecent().args; + var text = cbArgs[0]; + var status = cbArgs[1]; + var error = cbArgs[2]; + var expectedText = ApiResponse.forma_alerts.iso.notfound.responseText; + + expect(cb.spy).toHaveBeenCalled(); + expect(text).toEqual(expectedText); + expect(status).toEqual('error'); + expect(error).toEqual(''); + }); + }); + + + describe("FORMA Alerts by country - Failure", function() { + var cb = null; + var url = 'http://beta.gfw-apis.appspot.com/forest-change/forma-alerts/admin/bra?period=YYYY%2CYYYY'; + + beforeEach(function(done) { + mps.publish( + 'analysis/get', + [{layerName: 'forma-alerts', iso: 'bra', period: 'YYYY,YYYY'}]); + + request = jasmine.Ajax.requests.mostRecent(); + expect(request.url).toBe(url); + expect(request.method).toBe('POST'); + expect(request.data()).toEqual({}); - expect(callback.spy).toHaveBeenCalled(); - response = callback.spy.calls.mostRecent().args[0]; - expect(response).toEqual(expected); + cb = { + spy: function(responseText, status, error) { + done(); + } + }; + + spyOn(cb, 'spy').and.callThrough(); + mps.subscribe('analysis/get-failure', cb.spy); + request.response(ApiResponse.forma_alerts.iso.failure); + }); + + it("The analysis/get-results cb called with correct API response", function() { + var cbArgs = cb.spy.calls.mostRecent().args; + var text = cbArgs[0]; + var status = cbArgs[1]; + var error = cbArgs[2]; + var expectedText = ApiResponse.forma_alerts.iso.failure.responseText; + + expect(cb.spy).toHaveBeenCalled(); + expect(text).toEqual(expectedText); + expect(status).toEqual('error'); + expect(error).toEqual(''); }); }); }); From b3c83854824c7db1dea90c894a1468bd75c461a1 Mon Sep 17 00:00:00 2001 From: Aaron Date: Thu, 19 Jun 2014 12:49:18 -0700 Subject: [PATCH 107/823] Add initial local storage caching to nsa module --- app/assets/javascripts/map/nsa.js | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/app/assets/javascripts/map/nsa.js b/app/assets/javascripts/map/nsa.js index d2fb5cf8aa..e19008df08 100644 --- a/app/assets/javascripts/map/nsa.js +++ b/app/assets/javascripts/map/nsa.js @@ -4,8 +4,9 @@ */ define([ 'jquery', - 'mps' -], function ($, mps) { + 'mps', + 'store' +], function ($, mps, store) { return { /** @@ -17,17 +18,26 @@ define([ * @param {function} errorCb The error callback function * @return {object} The jqXHR object handle. */ - spy: function(url, data, successCb, errorCb) { + spy: function(url, data, successCb, errorCb, cache) { var jqxhr = null; - var key = null; var val = null; + if (cache && store.enabled) { + val = store.get(url); + if (val) { + successCb(val); + } + } + $.ajax({ url: url, type: "POST", data: JSON.stringify(data), success: function(response) { if (successCb) { + if (cache && store.enabled) { + store.set(url, response); + } successCb(response); } }, From 7867934a15edb8fa143e35eb50f527635a619395 Mon Sep 17 00:00:00 2001 From: Aaron Date: Thu, 19 Jun 2014 13:02:34 -0700 Subject: [PATCH 108/823] add cache parameter to analysis-get event --- app/assets/javascripts/map/analysis.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/app/assets/javascripts/map/analysis.js b/app/assets/javascripts/map/analysis.js index 0f0cd53e86..081443e4b6 100644 --- a/app/assets/javascripts/map/analysis.js +++ b/app/assets/javascripts/map/analysis.js @@ -29,8 +29,8 @@ define([ }, init: function() { - mps.subscribe('analysis/get', _.bind(function(config) { - this.execute(config); + mps.subscribe('analysis/get', _.bind(function(config, cache) { + this.execute(config, cache); }, this)); }, @@ -95,7 +95,7 @@ define([ * useid - Concession polygon cartodb_id (e.g., 2) * wdpa - WDPA polygon cartodb_id (e.g., 800) */ - execute: function(config) { + execute: function(config, cache) { var url = this.get_url(config); nsa.spy( @@ -106,7 +106,8 @@ define([ }, function(responseText, status, error) { mps.publish('analysis/get-failure', [responseText, status, error]); - }); + }, + cache); } }); From 927bb86051498be097d4dade2ca20bf7812404b3 Mon Sep 17 00:00:00 2001 From: pedro Date: Thu, 19 Jun 2014 23:57:13 +0200 Subject: [PATCH 109/823] new filter layer --- app/assets/javascripts/map.js | 7 +- .../javascripts/map/views/layersFilter.js | 378 ------------------ .../javascripts/map/views/layersMenu.js | 33 ++ app/assets/stylesheets/_base.scss | 8 +- app/assets/stylesheets/_grid.scss | 5 + app/assets/stylesheets/application.scss | 6 + .../stylesheets/modules/_mod-header-nav.scss | 62 +++ .../stylesheets/modules/_mod-layers-menu.scss | 88 ++++ app/assets/templates/layers_menu.html | 59 +++ app/views/layouts/map.html.erb | 23 +- app/views/map/index.html.erb | 1 + app/views/shared/_header_nav.html.erb | 37 ++ 12 files changed, 302 insertions(+), 405 deletions(-) delete mode 100644 app/assets/javascripts/map/views/layersFilter.js create mode 100644 app/assets/javascripts/map/views/layersMenu.js create mode 100644 app/assets/stylesheets/modules/_mod-header-nav.scss create mode 100644 app/assets/stylesheets/modules/_mod-layers-menu.scss create mode 100644 app/assets/templates/layers_menu.html create mode 100644 app/views/shared/_header_nav.html.erb diff --git a/app/assets/javascripts/map.js b/app/assets/javascripts/map.js index 7068227d56..aea6584b21 100644 --- a/app/assets/javascripts/map.js +++ b/app/assets/javascripts/map.js @@ -4,16 +4,19 @@ require([ 'router', 'presenter', 'mediator', + 'views/layersMenu', 'backbone', 'analysis', 'mps', - 'jasmine' -], function (app, router, presenter, mediator, Backbone, analysis, mps, jasmine) { + 'jasmine', +], function (app, router, presenter, mediator, layersMenu, Backbone, analysis, mps, jasmine) { console.log('Main entry point...', app); + if (!Backbone.History.started) { console.log('Backbone.history.start'); Backbone.history.start({pushState: true}); } + // For dev window.analysis = analysis; window.mps = mps; diff --git a/app/assets/javascripts/map/views/layersFilter.js b/app/assets/javascripts/map/views/layersFilter.js deleted file mode 100644 index d91f5fa735..0000000000 --- a/app/assets/javascripts/map/views/layersFilter.js +++ /dev/null @@ -1,378 +0,0 @@ -var Filter = (function() { - - var pids, - filters = [], - lastClass = null, - categories = [], - $filters = $('.filters'), - $layer = $('#layer'); - - function _remove(id) { - if (_.include(filters, id)) { - filters = _.without(filters, id); - } - - config.MAPOPTIONS.layers = filters; - } - - function _add(id) { - if (!_.include(filters, id)) { - filters.push(id); - } - - config.MAPOPTIONS.layers = filters; - } - - function _toggle(id) { - if (_.include(filters, id)) { - filters = _.without(filters, id); - } else { - filters.push(id); - } - - config.MAPOPTIONS.layers = filters; - } - - function _show(callback) { - if (!$filters.hasClass('hide')) return; - - var count = categories.length; - - $filters.fadeIn(150, function() { - $filters.find('li').each(function(i, el) { - $(el).delay(i * 50).animate({ opacity: 1 }, 150, 'easeInExpo', function() { - $(this).find('a').animate({ top: '-15px'}, 150); - count--; - - if (count <= 0) { - $filters.removeClass('hide'); - - if (callback) callback(); - _calcFiltersPosition(); - } - }); - }); - }); - } - - function _hide(callback) { - _hideLayer(); - - if ($filters.hasClass('hide')) { - callback && callback(); - - return; - } - - var count = categories.length; - - $($filters.find('li a').get().reverse()).each(function(i, el) { - $(el).delay(i * 50).animate({ top: '15px' }, 150, function() { - $(this).parent().animate({ opacity: '0'}, 150, function() { - --count; - - if (count <= 0) { - $filters.fadeOut(150, function() { - $filters.addClass('hide'); - - if (callback) callback(); - }); - } - }); - }); - }); - } - - function _calcFiltersPosition() { - $filters.find('li').each(function(i, el) { - $(el).data('left-pos', $(el).position().left+1); - }); - } - - function _hideLayer() { - $layer.animate({ opacity: 0 }, 70, function() { - $layer.css('left', -10000); - }); - } - - function _closeOpenFilter() { - var c = $layer.attr('class'); - - if (c === undefined) return; - - clearTimeout(pids); - - pids = setTimeout(function() { - _close(c); - }, 100); - } - - function _close(c) { - $layer.animate({ opacity: 0 }, 70, function() { - $layer.css('left', -10000); - $layer.removeClass(c); - }); - - $layer.css('left', '-10000px'); - } - - function _open() { - var $li = $(this), - lw = $layer.width(), - liClass = $li.attr('data-id'), - l = $li.data('left-pos'), - $line = $li.find('.filter-line'), - lineWidth = $line.width(); - - cancelClose(); - - $layer.removeClass(lastClass); - - var name = $li.find('a').text(); - $layer.find('.filter-title').text(name); - - var color = $li.find('a').css('color'); - $layer.find('.filter-title').css({ color: color }); - $layer.find('.filter-line').css({ backgroundColor: color }); - - $layer.find('.last').removeClass('last'); - $layer.find('.filter-links li').hide(); - $layer.find('.filter-links .'+liClass).show(); - $layer.find('.filter-links .'+liClass).last().addClass('last'); - - $('.filter-links .last').closest('li').css({ 'border-bottom': 0 }); - - $layer.addClass(liClass); - lastClass = liClass; - - var left = (l+$li.width() / 2) - (170 / 2), - height = $layer.find('.filter-links').height(); - - $layer.css({ left: left }); - - $layer.animate({ opacity: 1 }, 250); - $('.filter-scroll').css({ height: height }); - } - - function cancelClose() { - clearTimeout(pids); - } - - function _onMouseEnter() { - $layer.animate({ opacity: 1 }, 150); - } - - function _init() { - $(document).on('mouseenter', '.filters li', _open); - $layer.on('mouseleave', _closeOpenFilter); - - $(document).on('click', '.radio', function(e) { - e.preventDefault(); - - $('.radio[data-name="'+$(this).attr('data-name')+'"]').removeClass('checked'); - $(this).addClass('checked'); - }); - - $(document).on('click', '.checkbox', function(e) { - e.preventDefault(); - - $(this).toggleClass('checked'); - - if ($(this).hasClass('checked')) { - var color = $(this).attr('data-color'); - $(this).css('color', color); - $(this).find('i').css('background-color', color); - } else { - $(this).css('color', '#ccc'); - $(this).find('i').css('background-color', '#ccc'); - } - }); - } - - function _check(id) { - $('#layer a[data-id='+id +']').addClass('checked'); - - var color = $('#layer a[data-id='+id +']').attr('data-color'); - $('#layer a[data-id='+id +']').css('color', color ); - $('#layer a[data-id='+id +']').find('i').css('background-color', color ); - - filters.push(id); - } - - function _resize() { - var height = $('.filter-links').height(); - - $('.filter-scroll').animate({ height: height }, 150); - } - - function _addForestLossFilter(id, slug, category, name, options) { - var clickEvent = options.clickEvent || null, - disabled = options.disabled || false, - source = options.source || null, - category_color = options.category_color || '#ccc', - color = options.color || '#ccc', - subtitle = options.subtitle || ''; - - var cat = category.replace(/ /g, '_').toLowerCase(); - - if (!_.include(categories, cat)) { - var template = _.template($('#filter-template').html()), - $filter = $(template({ name: category, category: cat, data: cat, category_color: category_color })); - - $filters.find('filter-list').append($filter); - categories.push(cat); - } - - var layerItemTemplate = null, - $layerItem = null; - - layerItemTemplate = _.template($('#layer-item-checkbox-loss-template').html()); - - $layerItem = $(layerItemTemplate({ name: name, id: id, slug: slug, category: cat, disabled: disabled, source: source, color: color, subtitle: subtitle })); - - if (slug === 'loss' && config.BASELAYER === 'loss' || slug === 'forestgain' && _.include(config.MAPOPTIONS.layers, 596)) { - $layerItem.find('.checkbox').addClass('checked'); - - var color = $layerItem.find('.checkbox').attr('data-color'); - $layerItem.find('.checkbox').css('color', color ); - $layerItem.find('.checkbox').find('i').css('background-color', color ); - } - - $layerItem.find('.checkbox').on('click', function() { - clickEvent && clickEvent(); - }); - - $layer.find('.filter-links .extra').append($layerItem); - $layerItem.find('.checkbox').addClass(cat); - } - - function _addForestLossFilters(id, slug, category, name, options) { - var clickEvent = options.clickEvent || null, - disabled = options.disabled || false, - source = options.source || null, - category_color = options.category_color || '#ccc', - color = options.color || '#ccc', - subtitle = options.subtitle || ''; - - var cat = category.replace(/ /g, '_').toLowerCase(); - - if (!_.include(categories, cat)) { - var template = _.template($('#filter-template').html()), - $filter = $(template({ name: category, category: cat, data: cat, category_color: category_color })); - - $filters.find('ul').append($filter); - categories.push(cat); - } - - var layerItemTemplate = null, - $layerItem = null; - - layerItemTemplate = _.template($('#layer-item-radio-loss-template').html()); - - $layerItem = $(layerItemTemplate({ name: name, id: id, slug:slug, category: cat, disabled: disabled, source: source, subtitle: subtitle })); - - if (config.BASELAYER === 'loss' || _.include(config.MAPOPTIONS.layers, 596)) { - $layerItem.find('.radio').addClass('checked'); - } else { - $layerItem.find('.extra').hide(); - } - - $layerItem.find('.radio').on('click', function(e) { - e.preventDefault(); - - if (!$(this).hasClass('checked')) { - $layerItem.parent().find('.extra').slideUp(); - $layerItem.find('.extra').slideDown(150); - - setTimeout(function() { _resize(); }, 300); - - clickEvent && clickEvent(); - - $(this).parent().find('.checkbox').each(function(i, c) { - var $c = $(c); - $c.addClass('checked'); - var color = $c.attr('data-color'); - $c.css('color', color ); - $c.find('i').css('background-color', color ); - }); - } - }); - - $layer.find('.filter-links').append($layerItem); - $layerItem.find('.checkbox').addClass(cat); - } - - function _addFilter(id, slug, category, name, options) { - var clickEvent = options.clickEvent || null, - disabled = options.disabled || false; - source = options.source || null; - category_color = options.category_color || '#ccc'; - color = options.color || '#ccc'; - subtitle = options.subtitle || ''; - - var cat = category.replace(/ /g, '_').toLowerCase(); - - if (!_.include(categories, cat)) { - var template = _.template($('#filter-template').html()), - $filter = $(template({ name: category, category: cat, data: cat, category_color: category_color })); - - $filters.find('ul').append($filter); - categories.push(cat); - } - - var layerItemTemplate = null, - $layerItem = null; - - if (!disabled) { - // Select the kind of input (radio or checkbox) depending on the category - if (cat === 'forest_change') { - layerItemTemplate = _.template($('#layer-item-radio-template').html()); - - $layerItem = $(layerItemTemplate({ name: name, id: id, slug:slug, category: cat, disabled: disabled, source: source, subtitle: subtitle })); - - $layerItem.find('.radio').on('click', function() { - $layerItem.parent().find('.extra').slideUp(); - setTimeout(function() { _resize(); }, 300); - - if (!$(this).find('.radio').hasClass('checked')) { - clickEvent && clickEvent(); - } - - }); - } else { - layerItemTemplate = _.template($('#layer-item-checkbox-template').html()); - $layerItem = $(layerItemTemplate({ name: name, id: id, color: color, slug:slug, category: cat, disabled: disabled, source: source })); - - $layerItem.find('a:not(.source)').on('click', function() { - clickEvent(); - }); - } - } else { - layerItemTemplate = _.template($('#layer-item-disabled-template').html()); - $layerItem = $(layerItemTemplate({ name: name, id: id, color: color, slug:slug, category: cat, disabled: disabled, source: source })); - } - - if ((slug === 'nothing' && config.BASELAYER === null && !_.include(config.MAPOPTIONS.layers, 596)) || (slug === config.BASELAYER)) { - $layerItem.find('.radio').addClass('checked'); - } - - $layer.find('.filter-links').append($layerItem); - $layerItem.find('.checkbox').addClass(cat); - } - - return { - init: _init, - show: _show, - hide: _hide, - addFilter: _addFilter, - addForestLossFilters: _addForestLossFilters, - addForestLossFilter: _addForestLossFilter, - toggle: _toggle, - remove: _remove, - add: _add, - closeOpenFilter:_closeOpenFilter, - calcFiltersPosition: _calcFiltersPosition, - check: _check - }; - -}()); diff --git a/app/assets/javascripts/map/views/layersMenu.js b/app/assets/javascripts/map/views/layersMenu.js new file mode 100644 index 0000000000..d8ad4f85dc --- /dev/null +++ b/app/assets/javascripts/map/views/layersMenu.js @@ -0,0 +1,33 @@ +/** + * The layers filter module. + * + * @return singleton instance of layers fitler class (extends Backbone.View). + */ +define([ + 'backbone', + 'underscore', + 'presenter', + 'mps', + 'text!layers_menu.html' +], function(Backbone, _, presenter, mps, layersMenuTpl) { + + var LayersMenu = Backbone.View.extend({ + + el: $('.layers-menu'), + template: _.template(layersMenuTpl), + + initialize: function() { + this.render(); + }, + + render: function() { + this.$el.append(this.template()); + } + + }); + + var layersMenu = new LayersMenu(); + + return layersMenu; + +}); \ No newline at end of file diff --git a/app/assets/stylesheets/_base.scss b/app/assets/stylesheets/_base.scss index afd5112610..507be3f968 100644 --- a/app/assets/stylesheets/_base.scss +++ b/app/assets/stylesheets/_base.scss @@ -8,6 +8,7 @@ html { body { min-width: 980px; border-top: 5px solid #222; + background: image-url('backgrounds/bkg_header.png') repeat-x top center; &.embed { min-width: 0; @@ -43,11 +44,8 @@ strong { font-weight: bold; } -.inner { - margin: 0 auto; - width: 960px; - padding: 0 10px; -} +/* =Fonts +----------------------------------------------- */ .serif { font-family: "Georgia", serif; diff --git a/app/assets/stylesheets/_grid.scss b/app/assets/stylesheets/_grid.scss index e69de29bb2..0c4bddb4d3 100644 --- a/app/assets/stylesheets/_grid.scss +++ b/app/assets/stylesheets/_grid.scss @@ -0,0 +1,5 @@ +.inner { + margin: 0 auto; + width: 960px; + padding: 0 10px; +} \ No newline at end of file diff --git a/app/assets/stylesheets/application.scss b/app/assets/stylesheets/application.scss index 62767d2f18..05b70faf17 100644 --- a/app/assets/stylesheets/application.scss +++ b/app/assets/stylesheets/application.scss @@ -2,6 +2,7 @@ //= require jquery.jscrollpane //= require tipsy +// Compass @import 'compass/reset'; @import "compass"; @import "compass/css3"; @@ -15,12 +16,14 @@ @import 'compass/css3/background-size'; @import 'compass/css3/images'; +// General @import '_settings'; @import '_mixins'; @import '_layout'; @import '_grid'; @import '_base'; +// Images @import 'icons/*.png'; @import 'logos/*.png'; @import 'home-icons/*.png'; @@ -29,6 +32,9 @@ @import 'country-icons/*.png'; @import 'stories-icons/*.png'; +// Modules +@import 'modules/_mod-header-nav'; +@import 'modules/_mod-layers-menu'; @import 'modules/_mod-form'; @import 'modules/_mod-map'; @import 'modules/countries'; diff --git a/app/assets/stylesheets/modules/_mod-header-nav.scss b/app/assets/stylesheets/modules/_mod-header-nav.scss new file mode 100644 index 0000000000..b714e4a7fc --- /dev/null +++ b/app/assets/stylesheets/modules/_mod-header-nav.scss @@ -0,0 +1,62 @@ +.header-nav { + height: 64px; + border-bottom: 1px solid #CFCFCF; + width: 100%; + + .header-nav__inner { + } + + .header-nav__logo { + z-index: 1050; + position: absolute; + height: 100px; + width: 207px; + margin-left: -10px; + @include logos-sprite(logo); + } + + .nav { + float: right; + margin-top: 18px; + + .nav__list, + #google_translate_element { + float: right; + margin-left: 10px; + } + + .nav__list__item { + float: left; + margin-left: 5px; + + a { + display: block; + height: 26px; + padding: 0 10px; + font-weight: bold; + font-size: 12px; + line-height: 27px; + text-transform: uppercase; + color: #333; + @include border-radius(3px); + + span { background:none; } + + &.selected { + background: $cGreen; + color: #fff; + + &:hover { + background: $cGreen; + } + } + + &:hover { + background: rgba($cGreen, .5); + } + } + } + } + + +} \ No newline at end of file diff --git a/app/assets/stylesheets/modules/_mod-layers-menu.scss b/app/assets/stylesheets/modules/_mod-layers-menu.scss new file mode 100644 index 0000000000..40c5b2e545 --- /dev/null +++ b/app/assets/stylesheets/modules/_mod-layers-menu.scss @@ -0,0 +1,88 @@ +$forest-change-color: rgb(255, 102, 153); +$forest-cover-color: rgb(178, 210, 110); +$forest-use-color: rgb(201, 142, 108); +$conservation-color: rgb(49, 130, 189); +$people-color: rgb(112, 125, 146); +$stories-color: rgb(242, 178, 87); + +.layers-menu { + height: 72px; + + .inner { + height: 100%; + position: relative; + } + + .categories-list { + position: absolute; + bottom: 0; + right: 0; + } + + .categories-list > li { + position: relative; + height: 100%; + float: left; + display: inline-block; + padding: 14px 12px; + cursor: pointer; + border-bottom: 4px solid; + margin-left: 1px; + + .category-name { + font-weight: bold; + font-size: 12px; + text-decoration: uppercase; + } + + &:hover .layers-nav { + display: inline-block; + } + } + + .categories-list > li { + &.forest-change .category-icon { @include home-icons-sprite(icon_forest_clearing); } + &.forest-cover .category-icon { @include home-icons-sprite(icon_forest_cover); } + &.forest-use .category-icon { @include home-icons-sprite(icon_forest_use); } + &.conservation .category-icon { @include home-icons-sprite(icon_conservation); } + &.people .category-icon { @include home-icons-sprite(icon_regrowth); } + &.stories .category-icon { @include home-icons-sprite(marker_exclamation); } + + &.forest-change { color: $forest-change-color; border-color: $forest-change-color; } + &.forest-cover { color: $forest-cover-color; border-color: $forest-cover-color; } + &.forest-use { color: $forest-use-color; border-color: $forest-use-color; } + &.conservation { color: $conservation-color; border-color: $conservation-color; } + &.people { color: $people-color; border-color: $people-color; } + &.stories { color: $stories-color; border-color: $stories-color; } + } + + .layers-nav { + display: none; + position: absolute; + z-index: 1000; + left: -21px; + top: -28px; + width: 170px; + background: white; + @include box-shadow(0px 0px 5px 1px rgba(#000, .15)); + + .layers-nav-header { + height: 82px; + border-bottom: 4px solid; + text-align: center; + + .category-icon { + display: inline-block; + margin: auto; + width: 16px; + height: 16px; + margin-top: 18px; + } + + .category-name { + display: block; + padding: 8px 0; + } + } + } +} \ No newline at end of file diff --git a/app/assets/templates/layers_menu.html b/app/assets/templates/layers_menu.html new file mode 100644 index 0000000000..a0d351f5ad --- /dev/null +++ b/app/assets/templates/layers_menu.html @@ -0,0 +1,59 @@ +
      +
        + +
      • + FOREST CHANGE +
        +
        +
        + FOREST CHANGE +
        +
          +
        • + UMD tree cover loss & gain +
        • +
        • + FORMA alerts + (monthly, 500m, humid tropics) +
        • +
        • + Imazon SAD alerts + (monthly, 250m, Brazilian Amazon) +
        • +
        • + QUICC alerts + (quarterly, 5km, <37 degrees north) +
        • + +
        • + NASA active fires + (past 7 days, 1km, global)) +
        • +
        • + None +
        • +
        +
        +
      • + +
      • + FOREST COVER +
      • + +
      • + FOREST USE +
      • +
      • + CONSERVATION +
      • + +
      • + PEOPLE +
      • + +
      • + STORIES +
      • + +
      +
      \ No newline at end of file diff --git a/app/views/layouts/map.html.erb b/app/views/layouts/map.html.erb index 874df13c3f..0cfefa41ea 100644 --- a/app/views/layouts/map.html.erb +++ b/app/views/layouts/map.html.erb @@ -26,28 +26,11 @@ "> -
      -
      - - <%= render 'shared/navbar' %> -
      -
      -
      - <%= yield :title %> - <%= yield :inner_header %> -
      - <%= yield :filters %> -
      - -
      - <%= yield %> -
      - - <%= render 'shared/footer' %> + <%= render 'shared/header_nav' %> +
      <%= yield %>
      + <%= requirejs_include_tag "map" %> - <%= render 'shared/js_templates' %> - <%= render 'shared/js_footer' %> \ No newline at end of file diff --git a/app/views/map/index.html.erb b/app/views/map/index.html.erb index 6a76af3c8e..5d2fbf11fa 100644 --- a/app/views/map/index.html.erb +++ b/app/views/map/index.html.erb @@ -1 +1,2 @@ +
      <%= render 'shared/map' %> \ No newline at end of file diff --git a/app/views/shared/_header_nav.html.erb b/app/views/shared/_header_nav.html.erb new file mode 100644 index 0000000000..fe75ffb012 --- /dev/null +++ b/app/views/shared/_header_nav.html.erb @@ -0,0 +1,37 @@ +
      +
      + + + +
      +
      + From 4bb395ec522ada34434274a3c2184c0584e8d17a Mon Sep 17 00:00:00 2001 From: pedro Date: Fri, 20 Jun 2014 00:04:08 +0200 Subject: [PATCH 110/823] changes --- app/assets/stylesheets/modules/_mod-layers-menu.scss | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/app/assets/stylesheets/modules/_mod-layers-menu.scss b/app/assets/stylesheets/modules/_mod-layers-menu.scss index 40c5b2e545..08990ec7c9 100644 --- a/app/assets/stylesheets/modules/_mod-layers-menu.scss +++ b/app/assets/stylesheets/modules/_mod-layers-menu.scss @@ -84,5 +84,14 @@ $stories-color: rgb(242, 178, 87); padding: 8px 0; } } - } + + .layers-list > li { + font-size: 13px; + color: #ccc; + padding: 9px 10px 10px; + border-bottom: 1px solid #e5e5e5; + } + + + } // layers-nav } \ No newline at end of file From a61b09931c800278d77b3d5fced00d258a528356 Mon Sep 17 00:00:00 2001 From: pedro Date: Fri, 20 Jun 2014 00:14:27 +0200 Subject: [PATCH 111/823] filter menu more stuff --- .../stylesheets/modules/_mod-layers-menu.scss | 11 ++- app/assets/templates/layers_menu.html | 87 +++++++++++++++++++ 2 files changed, 97 insertions(+), 1 deletion(-) diff --git a/app/assets/stylesheets/modules/_mod-layers-menu.scss b/app/assets/stylesheets/modules/_mod-layers-menu.scss index 08990ec7c9..33e0966e18 100644 --- a/app/assets/stylesheets/modules/_mod-layers-menu.scss +++ b/app/assets/stylesheets/modules/_mod-layers-menu.scss @@ -88,8 +88,17 @@ $stories-color: rgb(242, 178, 87); .layers-list > li { font-size: 13px; color: #ccc; - padding: 9px 10px 10px; + padding: 9px 10px 10px 30px; border-bottom: 1px solid #e5e5e5; + + .layer-title { + display: block; + } + + .layer-info { + font-size: 11px; + margin-top: 3px; + } } diff --git a/app/assets/templates/layers_menu.html b/app/assets/templates/layers_menu.html index a0d351f5ad..7433f7691a 100644 --- a/app/assets/templates/layers_menu.html +++ b/app/assets/templates/layers_menu.html @@ -3,6 +3,7 @@
    • FOREST CHANGE +
      @@ -38,21 +39,107 @@
    • FOREST COVER + +
      +
      +
      + FOREST CHANGE +
      +
        +
      • + Tree cover extent +
      • +
      • + Intact forest landscapes +
      • +
      • + Tropical forest carbon stock +
      • +
      +
    • FOREST USE + +
      +
      +
      + FOREST CHANGE +
      +
        +
      • + Logging +
      • +
      • + Mining +
      • +
      • + Oil palm +
      • +
      • + Wood fiber plantations +
      • +
      +
    • +
    • CONSERVATION + +
      +
      +
      + FOREST CHANGE +
      +
        +
      • + Protected areas +
      • +
      • + Biodiversity hotspots +
      • +
      +
    • PEOPLE + +
      +
      +
      + FOREST CHANGE +
      +
        +
      • + Resource Rights +
      • +
      • + Land Rights + Coming soon… +
      • +
      +
    • STORIES + +
      +
      +
      + FOREST CHANGE +
      +
        +
      • + User stories +
      • +
      • + Mongabay stories +
      • +
      +
    From e6c25a51bab19b444dd4a679420ecb615bdb5975 Mon Sep 17 00:00:00 2001 From: Aaron Date: Thu, 19 Jun 2014 16:34:25 -0700 Subject: [PATCH 112/823] adding nsa test coverage --- app/assets/javascripts/map/nsa.js | 1 + jstest/spec-runner.js | 2 +- jstest/spec/nsa_spec.js | 53 +++++++++++++++++++++++++++++++ 3 files changed, 55 insertions(+), 1 deletion(-) create mode 100644 jstest/spec/nsa_spec.js diff --git a/app/assets/javascripts/map/nsa.js b/app/assets/javascripts/map/nsa.js index e19008df08..44461d6f80 100644 --- a/app/assets/javascripts/map/nsa.js +++ b/app/assets/javascripts/map/nsa.js @@ -23,6 +23,7 @@ define([ var val = null; if (cache && store.enabled) { + // TODO: Key should be made from url+data val = store.get(url); if (val) { successCb(val); diff --git a/jstest/spec-runner.js b/jstest/spec-runner.js index a87af6e5c5..597b8e2880 100644 --- a/jstest/spec-runner.js +++ b/jstest/spec-runner.js @@ -81,7 +81,7 @@ require.config({ }); require( -['spec/analysis_spec', 'mock_ajax'], +[/*'spec/analysis_spec',*/ 'spec/nsa_spec', 'mock_ajax'], function(){ // NOOP }); \ No newline at end of file diff --git a/jstest/spec/nsa_spec.js b/jstest/spec/nsa_spec.js new file mode 100644 index 0000000000..63ee1de30d --- /dev/null +++ b/jstest/spec/nsa_spec.js @@ -0,0 +1,53 @@ +define([ + 'app', + 'nsa', + 'mps', + 'underscore', + 'helpers/api_responses', +], function(app, nsa, mps, _) { + + + describe("The nsa module", function() { + + var request = null; + var cache = null; + + beforeEach(function() { + jasmine.Ajax.install(); + }); + + + describe("foo", function() { + + var cb = null; + + beforeEach(function(done) { + cache = false; + cb = { + spy: function(response) { + done(); + } + }; + + nsa.spy('/foo', {}, cb.spy); + + request = jasmine.Ajax.requests.mostRecent(); + expect(request.url).toBe('/foo'); + expect(request.method).toBe('POST'); + expect(request.data()).toEqual({}); + + spyOn(cb, 'spy').and.callThrough(); + request.response(ApiResponse.forma_alerts.iso.success); + }); + + it("", function() { + var cbArgs = cb.spy.calls.mostRecent().args; + var text = cbArgs[0]; + var expectedText = 'foo'; + + expect(cb.spy).toHaveBeenCalled(); + expect(text).toEqual(expectedText); + }); + }); + }); +}); \ No newline at end of file From b8fd1a335488e43c2cb89a387dcda6eb17d2bd36 Mon Sep 17 00:00:00 2001 From: pedro Date: Fri, 20 Jun 2014 02:04:40 +0200 Subject: [PATCH 113/823] toggle layers working of forest2000, gain and loss --- app/assets/javascripts/map.js | 3 +- .../javascripts/map/collections/layers.js | 11 ++- app/assets/javascripts/map/mediator.js | 51 ++++------- app/assets/javascripts/map/presenter.js | 84 ++++++++++++++++++- .../javascripts/map/views/layersMenu.js | 21 +++-- app/assets/javascripts/map/views/map.js | 2 +- .../stylesheets/modules/_mod-layers-menu.scss | 5 +- app/assets/templates/layers_menu.html | 12 ++- 8 files changed, 134 insertions(+), 55 deletions(-) diff --git a/app/assets/javascripts/map.js b/app/assets/javascripts/map.js index aea6584b21..83525216a3 100644 --- a/app/assets/javascripts/map.js +++ b/app/assets/javascripts/map.js @@ -4,12 +4,11 @@ require([ 'router', 'presenter', 'mediator', - 'views/layersMenu', 'backbone', 'analysis', 'mps', 'jasmine', -], function (app, router, presenter, mediator, layersMenu, Backbone, analysis, mps, jasmine) { +], function (app, router, presenter, mediator, Backbone, analysis, mps, jasmine) { console.log('Main entry point...', app); if (!Backbone.History.started) { diff --git a/app/assets/javascripts/map/collections/layers.js b/app/assets/javascripts/map/collections/layers.js index 57baf13bd3..b5258dddb0 100644 --- a/app/assets/javascripts/map/collections/layers.js +++ b/app/assets/javascripts/map/collections/layers.js @@ -27,9 +27,9 @@ define([ return _.where(this.toJSON(), {category_name: 'Forest change'}) }, - getBaselayer: function(baselayer) { + getBaselayer: function(layerName) { var layer = _.where(this.toJSON(), {category_name: 'Forest change', - slug: baselayer}); + slug: layerName}); if (layer) { return layer[0]; } @@ -39,6 +39,13 @@ define([ return _.filter(this.toJSON(), function(layer) { return layer.category_name != 'Forest change'; }); + }, + + getSublayer: function(layerName) { + var layer = _.where(this.getSublayers(), {slug: layerName}); + if (layer) { + return layer[0]; + } } }); diff --git a/app/assets/javascripts/map/mediator.js b/app/assets/javascripts/map/mediator.js index 6071c2d081..2fa7e95de8 100644 --- a/app/assets/javascripts/map/mediator.js +++ b/app/assets/javascripts/map/mediator.js @@ -14,15 +14,17 @@ define([ 'presenter', 'collections/layers', 'views/map', + 'views/layersMenu', 'views/layers/loss', 'views/layers/gain', 'views/layers/forest', 'views/layers/imazon' -], function (_, Backbone, mps, Class, presenter, layersCollection, map, LossLayer, GainLayer, ForestLayer, ImazonLayer) { +], function (_, Backbone, mps, Class, presenter, layersCollection, map, layersMenu, LossLayer, GainLayer, ForestLayer, ImazonLayer) { var Mediator = Class.extend({ init: function() { - + var self = this; + // Listen to presenter events presenter.on('change:zoom', this.updateZoom, this); presenter.on('change:latLng', this.updateCenter, this); @@ -36,48 +38,25 @@ define([ this.layerViews = {}; }, - baselayersOpts: { - views: { - loss: LossLayer, - gain: GainLayer, - imazon: ImazonLayer - }, - allowedCombined: [ - ['loss', 'gain'] - ] - }, - - sublayersOpts: { - views: { - forest2000: ForestLayer - } + baselayersViews: { + loss: LossLayer, + umd_tree_loss_gain: GainLayer, + imazon: ImazonLayer }, - validateBaselayers: function() { - var baselayersArr = presenter.get('baselayers'), - valid = false; - - _.each(this.baselayersOpts.allowedCombined, - function(layersArr) { - if (baselayersArr.length == 1 || - ($(baselayersArr).not(layersArr).length === 0 && - $(layersArr).not(baselayersArr).length === 0)) { - valid = true; - } - }); - - return valid; + sublayersViews: { + forest2000: ForestLayer }, updateBaselayers: function() { var self = this, baselayersArr = presenter.get('baselayers'); - if (this.validateBaselayers()) { + if (presenter.validateBaselayers(baselayersArr)) { // render baselayers _.each(baselayersArr, function(layerName) { if (!self.layerViews[layerName]) { - self.layerViews[layerName] = new self.baselayersOpts.views[layerName](); + self.layerViews[layerName] = new self.baselayersViews[layerName](); } self.layerViews[layerName].render(); }); @@ -104,7 +83,7 @@ define([ if (layer) { var layerName = layer.get('slug'); if (!self.layerViews[layerName]) { - self.layerViews[layerName] = new self.sublayersOpts.views[layerName](); + self.layerViews[layerName] = new self.sublayersViews[layerName](); } self.layerViews[layerName].render(); } @@ -112,7 +91,7 @@ define([ // remove sublayers _.each(layersCollection.getSublayers(), function(layer) { - if (sublayersArr.indexOf(layer.id.toString()) == -1) { + if (sublayersArr.indexOf(layer.id) == -1) { if (self.layerViews[layer.slug]) { self.layerViews[layer.slug].removeLayer(); } @@ -147,4 +126,4 @@ define([ return mediator; -}); +}); \ No newline at end of file diff --git a/app/assets/javascripts/map/presenter.js b/app/assets/javascripts/map/presenter.js index 3ebef8338a..041a20b507 100644 --- a/app/assets/javascripts/map/presenter.js +++ b/app/assets/javascripts/map/presenter.js @@ -8,16 +8,30 @@ define([ 'backbone', 'mps', -], function (Backbone, mps) { + 'collections/layers' +], function (Backbone, mps, layersCollection) { var Presenter = Backbone.Model.extend({ + baselayersOpts: { + allowedCombined: [ + ['loss', 'umd_tree_loss_gain'] + ] + }, + initialize: function() { + var self = this; + this.on('change', this.updateUrl, this); + + mps.subscribe('presenter/toggle-layer', function(layerName) { + self.toggleLayer(layerName); + }); }, /** * Set the presenter object with the passed attributes. + * * @param attrs Router params object. */ setFromUrl: function(attrs) { @@ -30,7 +44,7 @@ define([ latLng: latLng || [15.00, 27.00], iso: attrs.iso || 'ALL', maptype: attrs.maptype || 'terrain', - baselayers: baselayers || ['loss', 'gain'], + baselayers: baselayers || ['loss', 'umd_tree_loss_gain'], sublayers: sublayers || [] }; @@ -56,7 +70,71 @@ define([ trigger: false }; mps.publish('navigate', [place]); - } + }, + + /** + * Toggle a layer on the presenter + * + * @param {string} LayerName. + */ + toggleLayer: function(layerName) { + if (layersCollection.getBaselayer(layerName)) { + this.toggleBaselayer(layerName); + } else if (layersCollection.getSublayer(layerName)) { + this.toggleSublayer(layerName, layersCollection.getSublayer(layerName).id); + } + }, + + toggleBaselayer: function(layerName) { + var currentBaselayers = _.clone(presenter.get('baselayers')), + layerIndex = currentBaselayers.indexOf(layerName); + + if (layerIndex > -1) { + currentBaselayers.splice(layerIndex, 1); + presenter.set('baselayers', currentBaselayers); + } else { + var combined = presenter.get('baselayers').concat(layerName); + + if (this.validateBaselayers(combined)) { + presenter.set('baselayers', combined); + } else { + presenter.set('baselayers', [layerName]); + } + } + }, + + toggleSublayer: function(layerName, layerId) { + var currentSublayers = _.clone(presenter.get('sublayers')); + layerIndex = currentSublayers.indexOf(layerId); + + if (layerIndex > -1) { + currentSublayers.splice(layerIndex, 1); + presenter.set('sublayers', currentSublayers); + } else { + var combined = presenter.get('sublayers').concat(layerId); + presenter.set('sublayers', combined); + } + }, + + /** + * Validates baselayers are valid. + * + * @param {array} Baselayers array. + */ + validateBaselayers: function(baselayersArr) { + var valid = false; + + _.each(this.baselayersOpts.allowedCombined, + function(layersArr) { + if (baselayersArr.length == 1 || + ($(baselayersArr).not(layersArr).length === 0 && + $(layersArr).not(baselayersArr).length === 0)) { + valid = true; + } + }); + + return valid; + }, }); diff --git a/app/assets/javascripts/map/views/layersMenu.js b/app/assets/javascripts/map/views/layersMenu.js index d8ad4f85dc..38fa5d7b96 100644 --- a/app/assets/javascripts/map/views/layersMenu.js +++ b/app/assets/javascripts/map/views/layersMenu.js @@ -6,23 +6,34 @@ define([ 'backbone', 'underscore', - 'presenter', - 'mps', 'text!layers_menu.html' -], function(Backbone, _, presenter, mps, layersMenuTpl) { +], function(Backbone, _, layersMenuTpl) { var LayersMenu = Backbone.View.extend({ - el: $('.layers-menu'), + el: '.layers-menu', template: _.template(layersMenuTpl), + events: { + 'click .layer-title': 'onClickLayer', + 'click .radio': 'onClickLayer' + }, + initialize: function() { this.render(); }, render: function() { this.$el.append(this.template()); - } + }, + + onClickLayer: function(event) { + var layerName = $(event.currentTarget).parent().data('layer'); + + if (layerName) { + mps.publish('presenter/toggle-layer', [layerName]); + } + }, }); diff --git a/app/assets/javascripts/map/views/map.js b/app/assets/javascripts/map/views/map.js index 9b199e8c69..252dacd847 100644 --- a/app/assets/javascripts/map/views/map.js +++ b/app/assets/javascripts/map/views/map.js @@ -10,7 +10,7 @@ define([ 'underscore', 'presenter', 'gmap', - 'mps' + 'mps', ], function(Backbone, _, presenter, gmap, mps) { var Map = Backbone.View.extend({ diff --git a/app/assets/stylesheets/modules/_mod-layers-menu.scss b/app/assets/stylesheets/modules/_mod-layers-menu.scss index 33e0966e18..3e98031ed3 100644 --- a/app/assets/stylesheets/modules/_mod-layers-menu.scss +++ b/app/assets/stylesheets/modules/_mod-layers-menu.scss @@ -7,7 +7,7 @@ $stories-color: rgb(242, 178, 87); .layers-menu { height: 72px; - + .inner { height: 100%; position: relative; @@ -90,8 +90,10 @@ $stories-color: rgb(242, 178, 87); color: #ccc; padding: 9px 10px 10px 30px; border-bottom: 1px solid #e5e5e5; + cursor: default; .layer-title { + cursor: pointer; display: block; } @@ -101,6 +103,5 @@ $stories-color: rgb(242, 178, 87); } } - } // layers-nav } \ No newline at end of file diff --git a/app/assets/templates/layers_menu.html b/app/assets/templates/layers_menu.html index 7433f7691a..c3bc81c9b3 100644 --- a/app/assets/templates/layers_menu.html +++ b/app/assets/templates/layers_menu.html @@ -10,14 +10,18 @@ FOREST CHANGE
      -
    • - UMD tree cover loss & gain +
    • + UMD tree cover loss +
    • +
    • + UMD tree cover gain
    • FORMA alerts (monthly, 500m, humid tropics)
    • -
    • +
    • +
      Imazon SAD alerts (monthly, 250m, Brazilian Amazon)
    • @@ -46,7 +50,7 @@ FOREST CHANGE
      -
    • +
    • Tree cover extent
    • From d7995074beba8c7378fc3272710e7da9ab100514 Mon Sep 17 00:00:00 2001 From: pedro Date: Fri, 20 Jun 2014 02:17:23 +0200 Subject: [PATCH 114/823] changes --- app/assets/javascripts/map/mediator.js | 32 +++++++++---------- app/assets/javascripts/map/presenter.js | 13 ++++++-- .../stylesheets/modules/_mod-layers-menu.scss | 2 ++ app/assets/templates/layers_menu.html | 8 ++--- 4 files changed, 31 insertions(+), 24 deletions(-) diff --git a/app/assets/javascripts/map/mediator.js b/app/assets/javascripts/map/mediator.js index 2fa7e95de8..17f243e41e 100644 --- a/app/assets/javascripts/map/mediator.js +++ b/app/assets/javascripts/map/mediator.js @@ -52,25 +52,23 @@ define([ var self = this, baselayersArr = presenter.get('baselayers'); - if (presenter.validateBaselayers(baselayersArr)) { - // render baselayers - _.each(baselayersArr, function(layerName) { - if (!self.layerViews[layerName]) { - self.layerViews[layerName] = new self.baselayersViews[layerName](); - } - self.layerViews[layerName].render(); - }); - - // remove baselayers - _.each(layersCollection.getBaselayers(), function(layer) { - if (baselayersArr.indexOf(layer.slug) == -1) { - if (self.layerViews[layer.slug]) { - self.layerViews[layer.slug].removeLayer(); - } + // render baselayers + _.each(baselayersArr, function(layerName) { + if (!self.layerViews[layerName]) { + self.layerViews[layerName] = new self.baselayersViews[layerName](); + } + self.layerViews[layerName].render(); + }); + + // remove baselayers + _.each(layersCollection.getBaselayers(), function(layer) { + if (baselayersArr.indexOf(layer.slug) == -1) { + if (self.layerViews[layer.slug]) { + self.layerViews[layer.slug].removeLayer(); } - }); + } + }); - } }, updateSublayer: function() { diff --git a/app/assets/javascripts/map/presenter.js b/app/assets/javascripts/map/presenter.js index 041a20b507..da74b61322 100644 --- a/app/assets/javascripts/map/presenter.js +++ b/app/assets/javascripts/map/presenter.js @@ -35,10 +35,15 @@ define([ * @param attrs Router params object. */ setFromUrl: function(attrs) { - var baselayers = (attrs.baselayers) ? attrs.baselayers.split(',') : null, + var baselayers = null, sublayers = (attrs.sublayers) ? attrs.sublayers.split(',') : null, latLng = (attrs.lat && attrs.lng) ? [attrs.lat, attrs.lng] : null; + if (attrs.baselayers && + this.validateBaselayers(attrs.baselayers.split(','))) { + baselayers = attrs.baselayers.split(','); + } + var results = { zoom: attrs.zoom || 3, latLng: latLng || [15.00, 27.00], @@ -90,8 +95,10 @@ define([ layerIndex = currentBaselayers.indexOf(layerName); if (layerIndex > -1) { - currentBaselayers.splice(layerIndex, 1); - presenter.set('baselayers', currentBaselayers); + if (currentBaselayers.length > 1) { + currentBaselayers.splice(layerIndex, 1); + presenter.set('baselayers', currentBaselayers); + } } else { var combined = presenter.get('baselayers').concat(layerName); diff --git a/app/assets/stylesheets/modules/_mod-layers-menu.scss b/app/assets/stylesheets/modules/_mod-layers-menu.scss index 3e98031ed3..ecfd62d36e 100644 --- a/app/assets/stylesheets/modules/_mod-layers-menu.scss +++ b/app/assets/stylesheets/modules/_mod-layers-menu.scss @@ -54,6 +54,8 @@ $stories-color: rgb(242, 178, 87); &.conservation { color: $conservation-color; border-color: $conservation-color; } &.people { color: $people-color; border-color: $people-color; } &.stories { color: $stories-color; border-color: $stories-color; } + + &.people .layers-nav { left: -50px; } } .layers-nav { diff --git a/app/assets/templates/layers_menu.html b/app/assets/templates/layers_menu.html index c3bc81c9b3..6e0e9a8cc8 100644 --- a/app/assets/templates/layers_menu.html +++ b/app/assets/templates/layers_menu.html @@ -69,7 +69,7 @@
      - FOREST CHANGE + FOREST USE
      • @@ -94,7 +94,7 @@
        - FOREST CHANGE + CONSERVATION
        • @@ -113,7 +113,7 @@
          - FOREST CHANGE + PEOPLE
          • @@ -133,7 +133,7 @@
            - FOREST CHANGE + STORIES
            • From be7c61768547247703b048705afcd48d99367ca4 Mon Sep 17 00:00:00 2001 From: pedro Date: Fri, 20 Jun 2014 02:18:55 +0200 Subject: [PATCH 115/823] hot fix --- app/assets/templates/layers_menu.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/assets/templates/layers_menu.html b/app/assets/templates/layers_menu.html index 6e0e9a8cc8..eb73df220a 100644 --- a/app/assets/templates/layers_menu.html +++ b/app/assets/templates/layers_menu.html @@ -47,7 +47,7 @@
              - FOREST CHANGE + FOREST COVER
              • From 6c936aed9955429676333906e63cfb220cd703f4 Mon Sep 17 00:00:00 2001 From: Adrian Date: Thu, 19 Jun 2014 17:26:32 -0700 Subject: [PATCH 116/823] refactoring: added ui for searchbox, legend and analysis_button --- .../javascripts/map/views/analysis_button.js | 32 ++ app/assets/javascripts/map/views/legend.js | 403 +----------------- app/assets/javascripts/map/views/map.js | 12 +- app/assets/javascripts/map/views/searchbox.js | 32 ++ .../templates/analysis_control_tpl.html | 9 + ...{analysis.html => analysis_whole_tpl.html} | 0 app/assets/templates/legend.html | 139 +----- app/assets/templates/searchbox.html | 5 + 8 files changed, 113 insertions(+), 519 deletions(-) create mode 100644 app/assets/javascripts/map/views/analysis_button.js create mode 100644 app/assets/javascripts/map/views/searchbox.js create mode 100644 app/assets/templates/analysis_control_tpl.html rename app/assets/templates/{analysis.html => analysis_whole_tpl.html} (100%) create mode 100644 app/assets/templates/searchbox.html diff --git a/app/assets/javascripts/map/views/analysis_button.js b/app/assets/javascripts/map/views/analysis_button.js new file mode 100644 index 0000000000..0fa712c900 --- /dev/null +++ b/app/assets/javascripts/map/views/analysis_button.js @@ -0,0 +1,32 @@ +/** + * The layers filter module. + * + * @return singleton instance of layers fitler class (extends Backbone.View). + */ +define([ + 'backbone', + 'underscore', + 'presenter', + 'mps', + 'text!analysis_control_tpl.html' +], function(Backbone, _, presenter, mps, analysisTpl) { + + var AnalysisButton = Backbone.View.extend({ + + template: _.template(analysisTpl), + + initialize: function() { + this.render(); + }, + + render: function() { + this.$el.append(this.template()); + } + + }); + + var AnalysisButton = new AnalysisButton(); + + return AnalysisButton; + +}); \ No newline at end of file diff --git a/app/assets/javascripts/map/views/legend.js b/app/assets/javascripts/map/views/legend.js index 52615a3a54..f1bdc009f8 100644 --- a/app/assets/javascripts/map/views/legend.js +++ b/app/assets/javascripts/map/views/legend.js @@ -1,389 +1,32 @@ -gfw.ui.model.LegendItem = Backbone.Model.extend(); +/** + * The layers filter module. + * + * @return singleton instance of layers fitler class (extends Backbone.View). + */ +define([ + 'backbone', + 'underscore', + 'presenter', + 'mps', + 'text!legend.html' +], function(Backbone, _, presenter, mps, template) { -gfw.ui.collection.LegendItems = Backbone.Collection.extend({ - model: gfw.ui.model.LegendItem -}); + var Legend = Backbone.View.extend({ -gfw.ui.model.Legend = Backbone.Model.extend({ - defaults: { - hidden: true, - layerCount: 0 - } -}); + template: _.template(template), -gfw.ui.view.Legend = gfw.ui.view.Widget.extend({ - className: 'legend', + initialize: function() { + this.render(); + }, - events: { - 'click .toggle': '_toggleOpen' - }, - - initialize: function() { - _.bindAll(this, 'add', 'replace', 'toggle', 'toggleOpen', 'toggleDraggable', 'onStopDragging', 'addContent', 'removeContent'); - - this.options = _.extend(this.options, this.defaults); - - this.model = new gfw.ui.model.Legend(); - - this.add_related_model(this.model); - - this.model.bind('change:hidden', this.toggle); - this.model.bind('change:closed', this.toggleOpen); - this.model.bind('change:draggable', this.toggleDraggable, this); - - this.model.set('containment', '#map-container .map'); - var template = $('#legend-template').html(); - - this.template = new cdb.core.Template({ - template: template, - type: 'mustache' - }); - - this.categories = {}; - }, - - addScroll: function() { - if (!this.model.get('scrollbar')) { - this.model.set('scrollbar', true); - this.$el.find('.content').jScrollPane(); - this.api = this.$el.find('.content').data('jsp'); - } else { - this.refreshScroll(); - } - }, - - refreshScroll:function() { - if (this.api) this.api.reinitialise(); - }, - - increaseLayerCount: function() { - this.model.set('layerCount', this.model.get('layerCount') + 1); - }, - - decreaseLayerCount: function() { - this.model.set('layerCount', this.model.get('layerCount') - 1); - }, - - show: function(callback) { - if (!showMap) return; - - if (this.model.get('layerCount') === 0) { - this.model.set('hidden', true); - } else { - this.model.set('hidden', false); - } - - if (!this.model.get('closed')) this.resize(); - - callback && callback(); - - return this; - }, - - toggleItemBySlug: function(slug) { - var that = this; - - $('.legend .' + slug).toggle(); - - setTimeout( function() { that.resize(); }, 300); - }, - - toggleItem: function(id, category, category_title, title, slug, category_color, title_color, subevent) { - if(!this.categories[category] || !this.isAdded(id)) { - this.add(id, category, category_title, title, slug, category_color, title_color, subevent); - } else { - this.remove(id); - } - }, - - findItem: function(id) { - var foundItem = null; - - _.each(this.categories, function(c) { - _.each(c.models, function(item) { - if (item.get('cat_id') == id) foundItem = item; - }); - }); - - return foundItem; - }, - - isAdded: function(id) { - var duplicated = false; - - _.each(this.categories, function(c) { - _.each(c.models, function(item) { - if (item.get('cat_id') == id) duplicated = id; - }); - }); - - return duplicated; - }, - - replace: function(id, category, category_title, title, slug, category_color, title_color, subevent) { - var that = this; - - if(this.categories[category]) { - _.each(this.categories[category].models, function(c) { - that.removeContent(category, c.get('cat_id')); - that.categories[category].remove(c); - - that.decreaseLayerCount(); - }); - } - - this.add(id, category, category_title, title, slug, category_color, title_color, subevent); - }, - - removeCategory: function(category) { - var that = this; - - if(this.categories[category]) { - _.each(this.categories[category].models, function(c) { - if (c) { - that.removeContent(category, c.get('cat_id')); - that.categories[category].remove(c); - - that.decreaseLayerCount(); - } - }); - - if (that.categories[category].length == 0) { - delete that.categories[category]; - } - } - - setTimeout( function() { that.resize(); }, 300); - }, - - add: function(id, category, category_title, title, slug, category_color, title_color, subevent) { - var that = this; - - if (_.size(this.categories) && this.isAdded(id)) return; - - this.open(function() { - if (!that.categories[category]) { - that.categories[category] = new gfw.ui.collection.LegendItems; - that.categories[category].bind('add', that.addContent); - } - - var item = new gfw.ui.model.LegendItem({ - cat_id: id, - title: title, - slug: slug, - category: category, - category_title: category_title, - category_color: category_color, - title_color: title_color, - sublayer: subevent ? true : false, - subevent: subevent - }); - - that.categories[category].push( item ); - that.increaseLayerCount(); - if ($('body').hasClass('embed')){ - that.$el.css('left','-99999px'); - that.show(); - that.hide(); - } else { - that.show(); - } - }); - - }, - - remove: function(id) { - var that = this; - var item = this.findItem(id); - - if (!item) return; - - this.open(function() { - var category = item.get('category'); - - if (!that.categories[category]) return; - - that.categories[category].each(function(c) { - if (c && (c.get('cat_id') == id)) { - that.removeContent(category, id); - that.categories[category].remove(c); - - that.decreaseLayerCount(); - - if (that.categories[category].length == 0) { - delete that.categories[category]; - } - } - }); - - setTimeout( function() { that.resize(); }, 300); - }); - }, - - removeContent: function(category, id, hide) { - if (this.categories[category].length == 1) { - this.$el.find('ul.' + category).fadeOut(250, function() { - $(this).remove(); - }); - - if (!hide && _.size(this.categories) == 1) { - this.hide(); - } - } else { - this.$el.find('li#' + id).fadeOut(250, function() { - $(this).remove(); - }); - } - }, - - addContent: function(item) { - var that = this; - - var slug = item.get('slug'); - - if (this.categories[item.attributes.category].length == 1) { - var template_name; - - if (slug == 'imazon') { - template_name = 'legend-group-double-template'; - } else if (slug == 'umd_tree_loss_gain') { - template_name = 'legend-loss-template'; - } else { - template_name = 'legend-group-template'; - } - - var template = new cdb.core.Template({ - template: $('#' + template_name).html(), - type: 'mustache' - }); - - if (slug == 'forest2000') { - item.attributes.legend = 'Percent Tree Cover'; - item.attributes.legend_class = 'tree'; - - item.attributes.colors = [{ color: '#9DD89D', title: '25-50%' }, { color: '#4EC54E', title: '50-75%' }, { color: '#00B300', title: '75-100%' } ]; - } else if (slug == 'pantropical') { - item.attributes.legend = "Total Biomass (Mg C Ha-1)"; - item.attributes.legend_class = ''; - item.attributes.min = '75'; - item.attributes.max = '383'; - item.attributes.wide = [{ 'color_min': '#FFFFD4', 'color_max': '#dd8653'} ]; - } else { - item.attributes.legend = false; - } - - var $item = template.render(item.attributes); - - if (this.model.get('scrollbar')) { - this.$content.find('.jspPane').prepend( $item ); - } else { - this.$content.append( $item ); - } - - this.$content.find('li.' + item.attributes.category).fadeIn(250); - - if (config.BASELAYER === 'loss') { - this.$content.find('li#' + item.attributes.cat_id + '.loss').fadeIn(250); - } - - if (_.include(config.MAPOPTIONS.layers, 596)) { - this.$content.find('li#' + item.attributes.cat_id + '.forestgain').fadeIn(250); - } - } else { - var template_name; - - template_name = '#legend-item-template'; - - var template = new cdb.core.Template({ - template: $(template_name).html(), - type: 'mustache' - }); - - if (slug == 'forest2000') { - item.attributes.legend = 'Percent Tree Cover'; - item.attributes.legend_class = 'tree'; - item.attributes.colors = [{ color: '#9DD89D', title: '25-50%' }, { color: '#4EC54E', title: '50-75%' }, { color: '#00B300', title: '75-100%' } ]; - } else if (slug == 'pantropical') { - item.attributes.legend = "Total Biomass Carbon (Mg C Ha-1)"; - item.attributes.legend_class = ''; - item.attributes.min = '75'; - item.attributes.max = '383'; - item.attributes.wide = [{ 'color_min': '#FFFFD4', 'color_max': '#dd8653'} ]; - } else { - item.attributes.legend = false; - } - - var $item = template.render(item.attributes); - - this.$content.find('ul.' + item.attributes.category).append( $item ); - this.$content.find('li#' + item.attributes.cat_id).fadeIn(250); + render: function() { + this.$el.append(this.template()); } - setTimeout(function() { - that.$el.find('.checkbox').on('click', function(e) { - e.preventDefault(); - - item.attributes.subevent && item.attributes.subevent(); - }); - - that.resize(); - }, 300); - }, + }); - resize: function() { - var that = this; + var Legend = new Legend(); - var height = this.$el.find('.jspPane').height(); - - this.$content.animate({ height: height }, { duration: 100, complete: function() { - that.addScroll(); - }}); - }, - - toggleOpen: function() { - var that = this; - - if (this.model.get('closed')) { - that.model.set('contentHeight', that.$content.height()); - - if (that.model.get('layerCount') != 1) { - that.$layer_count.html( that.model.get('layerCount') + ' layers'); - } else { - that.$layer_count.html( that.model.get('layerCount') + ' layer'); - } - - that.$content.animate({ opacity: 0, height: 0 }, 250, function() { - that.$layer_count.fadeIn(250); - that.$shadow.fadeOut(250); - }); - - that.$el.addClass('closed'); - } else { - that.$layer_count.fadeOut(250, function() { - that.$content.animate({ opacity: 1, height: that.model.get('contentHeight') }, 250); - that.$shadow.fadeIn(250); - }); - - that.$el.removeClass('closed'); - } - }, - - render: function() { - var that = this; - this.$el.append(this.template.render( this.model.toJSON() )); - this.$content = this.$el.find('.content'); - this.$layer_count = this.$el.find('.layer_count'); - this.$shadow = this.$el.find('.shadow'); - - if ($('body').hasClass('embed')){ - this.$el.hide(); - setTimeout(function(){ - that.model.set('closed', true); - that.toggleOpen; - that.$el.css('left','60px').delay(300).fadeIn(); - },1000) - } + return Legend; - return this.$el; - } -}); +}); \ No newline at end of file diff --git a/app/assets/javascripts/map/views/map.js b/app/assets/javascripts/map/views/map.js index 9b199e8c69..dc22a5d6be 100644 --- a/app/assets/javascripts/map/views/map.js +++ b/app/assets/javascripts/map/views/map.js @@ -10,8 +10,11 @@ define([ 'underscore', 'presenter', 'gmap', - 'mps' -], function(Backbone, _, presenter, gmap, mps) { + 'mps', + 'views/analysis_button', + 'views/legend', + 'views/searchbox' +], function(Backbone, _, presenter, gmap, mps, analysis_button, legend, searchbox) { var Map = Backbone.View.extend({ initialize: function() { @@ -42,6 +45,11 @@ define([ // Listeners google.maps.event.addListener(this.map, 'zoom_changed', this.onZoomChange); google.maps.event.addListener(this.map, 'dragend', this.onCenterChange); + + var mapDiv = $('#map'); + mapDiv.append(analysis_button.$el); + mapDiv.append(legend.$el); + mapDiv.append(searchbox.$el); }, /** diff --git a/app/assets/javascripts/map/views/searchbox.js b/app/assets/javascripts/map/views/searchbox.js new file mode 100644 index 0000000000..8e43b5ff70 --- /dev/null +++ b/app/assets/javascripts/map/views/searchbox.js @@ -0,0 +1,32 @@ +/** + * The layers filter module. + * + * @return singleton instance of layers fitler class (extends Backbone.View). + */ +define([ + 'backbone', + 'underscore', + 'presenter', + 'mps', + 'text!searchbox.html' +], function(Backbone, _, presenter, mps, template) { + + var Searchbox = Backbone.View.extend({ + + template: _.template(template), + + initialize: function() { + this.render(); + }, + + render: function() { + this.$el.append(this.template()); + } + + }); + + var Searchbox = new Searchbox(); + + return Searchbox; + +}); \ No newline at end of file diff --git a/app/assets/templates/analysis_control_tpl.html b/app/assets/templates/analysis_control_tpl.html new file mode 100644 index 0000000000..e2bf0754c7 --- /dev/null +++ b/app/assets/templates/analysis_control_tpl.html @@ -0,0 +1,9 @@ + + +
                +

                Draw in the map the area you want to analyze

                +
                + Done + Cancel +
                +
                \ No newline at end of file diff --git a/app/assets/templates/analysis.html b/app/assets/templates/analysis_whole_tpl.html similarity index 100% rename from app/assets/templates/analysis.html rename to app/assets/templates/analysis_whole_tpl.html diff --git a/app/assets/templates/legend.html b/app/assets/templates/legend.html index a21ced9ea5..4698973f6f 100644 --- a/app/assets/templates/legend.html +++ b/app/assets/templates/legend.html @@ -1,141 +1,6 @@ - - - - - - - - - +
              \ No newline at end of file diff --git a/app/assets/templates/searchbox.html b/app/assets/templates/searchbox.html new file mode 100644 index 0000000000..a4f580e8c9 --- /dev/null +++ b/app/assets/templates/searchbox.html @@ -0,0 +1,5 @@ +
              + search + + +
              \ No newline at end of file From 209939e9a5604b35d2a7d8e5432b215b972803e5 Mon Sep 17 00:00:00 2001 From: pedro Date: Fri, 20 Jun 2014 02:30:30 +0200 Subject: [PATCH 117/823] hot fixes --- app/assets/javascripts/map/mediator.js | 3 ++- app/assets/javascripts/map/presenter.js | 4 ++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/app/assets/javascripts/map/mediator.js b/app/assets/javascripts/map/mediator.js index 17f243e41e..68620010b5 100644 --- a/app/assets/javascripts/map/mediator.js +++ b/app/assets/javascripts/map/mediator.js @@ -82,6 +82,7 @@ define([ var layerName = layer.get('slug'); if (!self.layerViews[layerName]) { self.layerViews[layerName] = new self.sublayersViews[layerName](); + self.layerViews[layerName].render(); } self.layerViews[layerName].render(); } @@ -89,7 +90,7 @@ define([ // remove sublayers _.each(layersCollection.getSublayers(), function(layer) { - if (sublayersArr.indexOf(layer.id) == -1) { + if (sublayersArr.indexOf(layer.id) <= -1) { if (self.layerViews[layer.slug]) { self.layerViews[layer.slug].removeLayer(); } diff --git a/app/assets/javascripts/map/presenter.js b/app/assets/javascripts/map/presenter.js index da74b61322..51f0eb1183 100644 --- a/app/assets/javascripts/map/presenter.js +++ b/app/assets/javascripts/map/presenter.js @@ -44,6 +44,10 @@ define([ baselayers = attrs.baselayers.split(','); } + _.each(sublayers, function(layerId, i) { + sublayers[i] = Number(layerId); + }); + var results = { zoom: attrs.zoom || 3, latLng: latLng || [15.00, 27.00], From 7604041dfd7052b67ae9762833ad042bd4d06443 Mon Sep 17 00:00:00 2001 From: Adrian Date: Thu, 19 Jun 2014 19:46:22 -0700 Subject: [PATCH 118/823] refactoring: search id fix --- app/assets/templates/search.html | 5 ----- app/assets/templates/searchbox.html | 2 +- 2 files changed, 1 insertion(+), 6 deletions(-) delete mode 100644 app/assets/templates/search.html diff --git a/app/assets/templates/search.html b/app/assets/templates/search.html deleted file mode 100644 index a9241a59d9..0000000000 --- a/app/assets/templates/search.html +++ /dev/null @@ -1,5 +0,0 @@ - \ No newline at end of file diff --git a/app/assets/templates/searchbox.html b/app/assets/templates/searchbox.html index a4f580e8c9..a4411f4fb8 100644 --- a/app/assets/templates/searchbox.html +++ b/app/assets/templates/searchbox.html @@ -1,4 +1,4 @@ -
              +
    + +
    +
    +
    + + <% if @country['extent'] > 0 %>

    <%= "#{@country['name'].titleize} has #{extent_to_human(@country['extent'])} hectares of tree cover.".html_safe %>

    <% end %> + +
    0 %>'> +

    The type of forest in this country is...

    +
    + + <% if @country['country_alt'].present? %> +

    <%= @country['country_alt'].html_safe %>

    + <% end %>
    +
    - -
    -
    - - -
    -
    Not available for this country
    -
    - -
    -
    -

    There were FORMA alerts in

    - -
    - Forest Clearing Alerts - Humid Tropics -
    +<% if @country['gva'].present? && @country['gva'] > 0 %> +
    +
    +
    0 && @country['employment'] < 25 %>'> +

    Economic value of <%= @country['name'].titleize %>'s forestry sector

    -
    - -
    - Download data -
    + <% gva_percent = (@country['gva_percent'] < 0.1) ? number_to_percentage(@country['gva_percent'], precision: 2) : number_to_percentage(@country['gva_percent'], precision: 1) %> -
    -
    -
    -
    +

    <%= "The forestry sector contributed USD #{gva_to_human(@country['gva'])} to the economy in 2006, which is approximately #{gva_percent} of the GDP.".html_safe %>

    -
    - -
    -
    - - -
    -
    No data available
    -
    -
    - Forest Type: -
      -
    • Regenerated
    • -
    • Primary
    • -
    • Planted
    • -
    -
    -
    -
    -
    -
    - - <% if @country['country_alt'].present? %> -
    -

    <%= @country['country_alt'].html_safe %>

    -
    - <% end %> -
    -
    -
    + <% employees = @country['employment'] %> - -
    -
    - - -
    - <% if @country['gva'].present? && @country['gva'] > 0 %> -

    - The forestry sector contributed USD <%= gva_to_human(@country['gva'])%> to the economy in 2006, which is appoximately <%= @country['gva_percent'] %> of the GDP. -

    - <% else %> -
    No data available
    - <% end %> -
    -
    -
    + <% if employees.present? && employees > 0 %> +
      '> +
    • +

      Employment

      - -
      -
      - - -
      - <% if @employees.present? && @employees > 0 %> -
      - <% if @employees < 1000 %> -

      <%= @employees %>
      thousand people are directly employed by the forestry sector, according to 2006 FAO data.

      + <% if employees < 1000 %> +

      <%= employees %>
      thousand people are directly employed by the forestry sector, according to 2006 FAO data

      <% else %> -

      <%= (@employees/1000.00).round(2) %>
      million people are directly employed by the forestry sector, according to 2006 FAO data.

      +

      <%= (employees/1000.00).round(2) %>
      million people are directly employed by the forestry sector, according to 2006 FAO data

      <% end %> -
        '> -
      • - <% @employees = @employees < 100 ? @employees : 100 %> -
        - <% @employees.times do |i| %> - <%= image_tag 'countries/man.png' %> - <% end %> - - <%= '...' if @employees == 100 %> -
        -
      • -
      -
      - <% else %> -
      No data available
      - <% end %> -
      -
      -
      - - -
      -
      - - -
      -
      No data available
      -
      -
      -
      -
      + <% employees = employees < 100 ? employees : 100 %> +
      + <% employees.times do |i| %> + <%= image_tag 'countries/man.png' %> + <% end %> - -
      -
      - - -
      - <% if @country['national_policy_link'].present? %> - - <% end %> + <%= '...' if employees == 100 %> +
      +
    • +
    + <% end %> + + +<% end %> - Are we missing a link? - - - +
    +
    +

    Forest tenure

    +
    +
    - -
    -
    +
    +

    Forest policy and legislation

    +
      + <% if @country['national_policy_link'].present? %> +
    • <%= link_to @country['national_policy_title'].present? ? @country['national_policy_title'] : 'National Forest Policy', @country['national_policy_link'] %>
    • + <% end %> +
    + + Are we missing a link? +
    +
    - +
    +
    +
      <% if @country['carbon_stocks'].present? && @country['carbon_stocks'] != 0 %> -
      - - -
      -

      This country has <%= number_with_delimiter(@country['carbon_stocks']) %> million metric tons of carbon stocks in living forest biomass.

      -
      -
      +
    • '> +

      Carbon stocks

      +

      This country has <%= number_with_delimiter(@country['carbon_stocks']) %> million
      metric tons of carbon stocks
      in living forest biomass.

      +
    • <% end %> - <% if @country['emissions'].present? && @country['emissions'] != 0 %> -
      +
    • last'> +

      GHG emissions

      - + <% precission = (@country['emissions'].abs.to_i < 0.1) ? 2 : 1 %> - <% precission = (@country['emissions'].abs.to_i < 0.1) ? 2 : 1 %> - -
      <% if @country['emissions'] > 0 %> -

      According to FAO data, <%= number_to_percentage(@country['emissions'], precision: precission) %> of GHG emissions in this country came from land-use change and forestry in 2011.

      +

      According to FAO data, <%= number_to_percentage(@country['emissions'], precision: precission) %> of GHG emissions in this country came from land-use change and forestry in 2011.

      <% else %> -

      According to FAO data, land-use change and forestry sequestered <%= number_to_percentage(@country['emissions'].abs, precision: precission) %> of this country’s GHG emissions in 2011.

      +

      According to FAO data, land-use change and forestry sequestered <%= number_to_percentage(@country['emissions'].abs, precision: precission) %> of this country’s GHG emissions in 2011.

      <% end %> -
      -
    • + <% end %> +
    +
    +
    - -
    - - -
    -
    - - -
    -
      - <% @conventions.each do |convention| %> - <% if @country["convention_#{convention}"].present? %> -
    • - <%= t('.conventions.' + convention + '_title_html') %> - <%= @country["convention_#{convention}"] %> -
    • - <% end %> - <% end %> -
    -
    -
    -
    - - - <% if @country['ministry_link'].present? || @country['external_links'].present? %> - - <% end %> + <% conventions = ['cbd', 'unfccc', 'kyoto', 'unccd', 'itta', 'cites', 'ramsar', 'world_heritage', 'nlbi', 'ilo'] %> - - + <% end %> + + + - -
    +<% if @country['ministry_link'].present? || @country['external_links'].present? %> + +
      + <% if @country['ministry_link'].present? %> +
    • ' target='_blank'><%= t('.ministry_link') %>
    • + <% end %> - -
      -
      -
      - - + <% if @country['external_links'].present? %> + <% JSON.parse(@country['external_links']).each do |link| %> +
    • <%= link_to link['title'], link['url'], :target => '_blank' %>
    • + <% end %> + <% end %> +
    +<% end %> -
    - <%= render 'shared/countries' %> - <%= render 'shared/sources' %> + + +
    +
    +

    <%= t('.country_blog.title', :country => @country['name'].titleize) %>

    + + +
    +
    + +
    +
    +
    + +
    +
    -
    \ No newline at end of file +
    + <%= render 'shared/countries' %> + <%= render 'shared/sources' %> +
    diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb index f60ebb5e9b..fd02f847b4 100644 --- a/app/views/layouts/application.html.erb +++ b/app/views/layouts/application.html.erb @@ -22,7 +22,6 @@ <%= favicon_link_tag 'favicon.ico' %> <%= stylesheet_link_tag "application", :media => "all" %> - <%= yield :css %> <%= controller_stylesheet_link_tag %> <%= javascript_include_tag "modernizr-2.6.2.min" %> <%= csrf_meta_tags %> diff --git a/config/routes.rb b/config/routes.rb index a39ef663aa..207194ce9e 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -27,9 +27,6 @@ # countries get '/countries' => 'countries#index' get '/country/:id' => 'countries#show', :as => 'country' - # todo => validate id - get '/country/:id/:area_id' => 'countries#show', :as => 'country_area' - get '/countries/overview' => 'countries#overview' # media diff --git a/lib/assets/javascripts/gfw/forest2000_tile_layer.js b/lib/assets/javascripts/gfw/forest2000_tile_layer.js index aea0efdba8..40be2bdb80 100644 --- a/lib/assets/javascripts/gfw/forest2000_tile_layer.js +++ b/lib/assets/javascripts/gfw/forest2000_tile_layer.js @@ -113,7 +113,6 @@ Forest2000TileLayerThreshold.prototype.filter_tile = function(canvas) { var srcY = 256 / Math.pow(2, zsteps) * (coord.y % Math.pow(2, zsteps)); var srcW = 256 / Math.pow(2, zsteps); var srcH = 256 / Math.pow(2, zsteps); - ctx.clearRect(0, 0, 256, 256); ctx.drawImage(canvas.image, srcX, srcY, srcW, srcH, 0, 0, 256, 256); } else { try { From 03454f740c141b3fba38a5f3ca213ba70cb0dcfd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A1n=20P=C3=A9rez?= Date: Wed, 25 Jun 2014 17:21:00 +0200 Subject: [PATCH 154/823] biodiversity layers and others, click button --- lib/assets/javascripts/gfw/ui/analysis.js.erb | 1 + .../javascripts/gfw/ui/infowindow.js.erb | 38 ++++++++++++++----- 2 files changed, 30 insertions(+), 9 deletions(-) diff --git a/lib/assets/javascripts/gfw/ui/analysis.js.erb b/lib/assets/javascripts/gfw/ui/analysis.js.erb index 2d22602ce9..bc29f37e43 100644 --- a/lib/assets/javascripts/gfw/ui/analysis.js.erb +++ b/lib/assets/javascripts/gfw/ui/analysis.js.erb @@ -754,6 +754,7 @@ gfw.ui.view.Analysis = cdb.core.View.extend({ }, _loadProtectedArea: function(the_geom,area_id){ + debugger; this.previous_layers = config.MAPOPTIONS.layers; this.loadProtectedAreaByWpaid(area_id, the_geom); }, diff --git a/lib/assets/javascripts/gfw/ui/infowindow.js.erb b/lib/assets/javascripts/gfw/ui/infowindow.js.erb index a979a32108..faab768d98 100644 --- a/lib/assets/javascripts/gfw/ui/infowindow.js.erb +++ b/lib/assets/javascripts/gfw/ui/infowindow.js.erb @@ -50,14 +50,11 @@ CartoDBInfowindow.prototype.draw = function() { '
    '+ '
    '; - div.innerHTML = this.template || this.template_base; + div.innerHTML = this.template_image || this.template_base; var a = this.getElementsByClassName("close", div)[0]; - var analyse = this.getElementsByClassName("analyse", div)[0]; + var analyse = this.getElementsByClassName("analyse", div); - google.maps.event.addDomListener(div, 'touchstart', function (ev) { - ev.preventDefault ? ev.preventDefault() : ev.returnValue = false; - }); google.maps.event.addDomListener(analyse, 'click', function (ev) { ev.preventDefault ? ev.preventDefault() : ev.returnValue = false; Analysis._loadProtectedArea(me.content.geom.the_geom, me.content.id, me.content.name); @@ -65,6 +62,25 @@ CartoDBInfowindow.prototype.draw = function() { me._hide(); }); + google.maps.event.addDomListener(a, 'click', function (ev) { + //ev.preventDefault ? ev.preventDefault() : ev.returnValue = false; + //me._hide(); + }); + + google.maps.event.addDomListener(div, 'click', function (ev) { + //ev.preventDefault ? ev.preventDefault() : ev.returnValue = false; + //ev.stopPropagation ? ev.stopPropagation() : window.event.cancelBubble = true; + }); + + google.maps.event.addDomListener(a, 'touchend', function (ev) { + ev.preventDefault ? ev.preventDefault() : ev.returnValue = false; + //me._hide(); + }); + + google.maps.event.addDomListener(div, 'touchstart', function (ev) { + ev.preventDefault ? ev.preventDefault() : ev.returnValue = false; + }); + google.maps.event.addDomListener(div, 'touchend', function (ev) { ev.preventDefault ? ev.preventDefault() : ev.returnValue = false; }); @@ -72,20 +88,16 @@ CartoDBInfowindow.prototype.draw = function() { google.maps.event.addDomListener(div, 'dblclick', function (ev) { ev.preventDefault ? ev.preventDefault() : ev.returnValue = false; }); - google.maps.event.addDomListener(div, 'mousedown', function (ev) { ev.preventDefault ? ev.preventDefault() : ev.returnValue = false; ev.stopPropagation ? ev.stopPropagation() : window.event.cancelBubble = true; }); - google.maps.event.addDomListener(div, 'mouseup', function (ev) { ev.preventDefault ? ev.preventDefault() : ev.returnValue = false; }); - google.maps.event.addDomListener(div, 'mousewheel', function (ev) { ev.stopPropagation ? ev.stopPropagation() : window.event.cancelBubble = true; }); - google.maps.event.addDomListener(div, 'DOMMouseScroll', function (ev) { ev.stopPropagation ? ev.stopPropagation() : window.event.cancelBubble = true; }); @@ -284,6 +296,14 @@ CartoDBInfowindow.prototype._show = function() { that._createScroll() }); }); + + $('#map').find('.protected-header .analyse').on('click', function(ev){ + ev.preventDefault ? ev.preventDefault() : ev.returnValue = false; + debugger; + Analysis._loadProtectedArea(me.content.geom.the_geom, me.content.id, me.content.name); + GFW.app._removeSpecialLayer(); + me._hide(); + }) } }; From 4829eb02bba38f4c2cbcf5d0923abc69c8723bd2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A1n=20P=C3=A9rez?= Date: Wed, 25 Jun 2014 17:24:57 +0200 Subject: [PATCH 155/823] Revert "Merge branch 'feature/umd-layer-intensity' of github.com:Vizzuality/gfw into feature/new-country-page" This reverts commit 487e2aa43210498f92ce67a6eb8e5d13f47b5131, reversing changes made to fdc9f2c1ae1bc4f35a8b9785ad0fb794a409987c. --- .../images/backgrounds/country-indepth.png | Bin 0 -> 2526 bytes .../backgrounds/wall_background_alpha.png | Bin 0 -> 21288 bytes .../images/country-icons-s2672b16f85.png | Bin 4814 -> 0 bytes .../images/country-icons-s2a34c6c91a.png | Bin 0 -> 8531 bytes .../images/country-icons/chart_grey.png | Bin 0 -> 448 bytes .../images/country-icons/country-indepth.png | Bin 0 -> 2526 bytes .../images/country-icons/download_grey.png | Bin 0 -> 325 bytes app/assets/images/country-icons/info_grey.png | Bin 0 -> 498 bytes app/assets/images/country-icons/share.png | Bin 0 -> 419 bytes app/assets/images/wall-background-alpha.png | Bin 0 -> 21288 bytes app/assets/javascripts/countries.js | 2791 +---------------- .../countries/models/countries_overview.js | 7 + .../javascripts/countries/models/country.js | 35 + .../javascripts/countries/views/embed.js | 358 +++ .../javascripts/countries/views/header.js | 538 ++++ .../javascripts/countries/views/index.js | 37 + .../javascripts/countries/views/overview.js | 1631 ++++++++++ .../javascripts/countries/views/show.js | 482 +++ app/assets/javascripts/embed_countries.js | 2 +- app/assets/stylesheets/application.scss | 14 + app/assets/stylesheets/countries.scss | 1618 +--------- app/assets/stylesheets/countries/index.scss | 97 + .../stylesheets/countries/overview.scss | 697 ++++ app/assets/stylesheets/countries/show.scss | 1007 ++++++ app/assets/stylesheets/home.scss | 2 +- app/controllers/countries_controller.rb | 19 +- app/helpers/countries_helper.rb | 19 +- app/views/countries/_header.html.erb | 66 + app/views/countries/show.html.erb | 561 ++-- app/views/layouts/application.html.erb | 1 + app/views/shared/_js_templates.html.erb | 7 +- config/routes.rb | 3 + .../gfw/deforestation_tile_layer.js | 24 +- .../javascripts/gfw/forest2000_tile_layer.js | 1 + 34 files changed, 5427 insertions(+), 4590 deletions(-) create mode 100644 app/assets/images/backgrounds/country-indepth.png create mode 100644 app/assets/images/backgrounds/wall_background_alpha.png delete mode 100644 app/assets/images/country-icons-s2672b16f85.png create mode 100644 app/assets/images/country-icons-s2a34c6c91a.png create mode 100644 app/assets/images/country-icons/chart_grey.png create mode 100644 app/assets/images/country-icons/country-indepth.png create mode 100644 app/assets/images/country-icons/download_grey.png create mode 100644 app/assets/images/country-icons/info_grey.png create mode 100644 app/assets/images/country-icons/share.png create mode 100644 app/assets/images/wall-background-alpha.png create mode 100644 app/assets/javascripts/countries/models/countries_overview.js create mode 100644 app/assets/javascripts/countries/models/country.js create mode 100644 app/assets/javascripts/countries/views/embed.js create mode 100644 app/assets/javascripts/countries/views/header.js create mode 100644 app/assets/javascripts/countries/views/index.js create mode 100644 app/assets/javascripts/countries/views/overview.js create mode 100644 app/assets/javascripts/countries/views/show.js create mode 100644 app/assets/stylesheets/countries/index.scss create mode 100644 app/assets/stylesheets/countries/overview.scss create mode 100644 app/assets/stylesheets/countries/show.scss create mode 100644 app/views/countries/_header.html.erb diff --git a/app/assets/images/backgrounds/country-indepth.png b/app/assets/images/backgrounds/country-indepth.png new file mode 100644 index 0000000000000000000000000000000000000000..3da4ab5d2e4b4f3f5adaeda2b13efd6b461e3f36 GIT binary patch literal 2526 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 literal 0 HcmV?d00001 diff --git a/app/assets/images/backgrounds/wall_background_alpha.png b/app/assets/images/backgrounds/wall_background_alpha.png new file mode 100644 index 0000000000000000000000000000000000000000..1ac0a5a102eb15807a6f4366a619f09a4fc44574 GIT binary patch literal 21288 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 literal 0 HcmV?d00001 diff --git a/app/assets/images/country-icons-s2672b16f85.png b/app/assets/images/country-icons-s2672b16f85.png deleted file mode 100644 index b55dc59d4940297238e6f802f56fcef57bbfe44d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4814 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 diff --git a/app/assets/images/country-icons-s2a34c6c91a.png b/app/assets/images/country-icons-s2a34c6c91a.png new file mode 100644 index 0000000000000000000000000000000000000000..7c164a4774a8697268d0814ddf8c18445ef9141e GIT binary patch literal 8531 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 literal 0 HcmV?d00001 diff --git a/app/assets/images/country-icons/chart_grey.png b/app/assets/images/country-icons/chart_grey.png new file mode 100644 index 0000000000000000000000000000000000000000..85abb134f294c4fbc9a4d863022eb8bac71b36b0 GIT binary patch literal 448 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 literal 0 HcmV?d00001 diff --git a/app/assets/images/country-icons/download_grey.png b/app/assets/images/country-icons/download_grey.png new file mode 100644 index 0000000000000000000000000000000000000000..f3454d42e228c6ce335168f8f3c982390b6bbda8 GIT binary patch literal 325 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 literal 0 HcmV?d00001 diff --git a/app/assets/javascripts/countries.js b/app/assets/javascripts/countries.js index dbc6658fd8..2126300988 100644 --- a/app/assets/javascripts/countries.js +++ b/app/assets/javascripts/countries.js @@ -6,2793 +6,4 @@ //= require gfw/ui/widget //= require gfw/ui/sourcewindow //= require gfw/ui/share - - -gfw.ui.view.CountriesShow = cdb.core.View.extend({ - el: document.body, - - events: { - 'click .info': '_openSource', - 'click .forma_dropdown-link': '_openDropdown', - 'click .hansen_dropdown-link': '_openDropdown', - 'click .hansen_dropdown-menu a': '_redrawCircle' - }, - - initialize: function() { - _.bindAll(this, '_positionScroll'); - - this.iso = this.options.iso; - - this.$nav = this.$('.country_menu'); - this.$indepth = this.$('.country_indepth'); - - this._stickynav(); - this._initViews(); - this._initHansenDropdown(); - }, - - _initViews: function() { - this.sourceWindow = new gfw.ui.view.SourceWindow(); - this.$el.append(this.sourceWindow.render()); - - - if (this.iso === 'CHN') { - var that = this; - - this._drawCountry(this.iso, function() { - that._drawCountry('TWN'); - }); - } else { - this._drawCountry(this.iso); - } - - this._drawForest(this.iso); - this._drawTenure(this.iso); - - this._drawCircle('forma', 'lines', { iso: this.iso }); - this._drawCircle('forest_loss', 'bars', { iso: this.iso, dataset: 'loss' }); - - Share = new gfw.ui.view.Share(); - this.$el.find('.country_graphs .inner').append(Share.render()); - }, - - _initFormaDropdown: function() { - $('.forma_dropdown-link').qtip({ - show: 'click', - hide: { - event: 'click unfocus' - }, - content: { - text: $('.forma_dropdown-menu') - }, - position: { - my: 'bottom right', - at: 'top right', - target: $('.forma_dropdown-link'), - adjust: { - x: -10 - } - }, - style: { - tip: { - corner: 'bottom right', - mimic: 'bottom center', - border: 1, - width: 10, - height: 6 - } - } - }); - }, - - _initHansenDropdown: function() { - this.dropdown = $('.hansen_dropdown-link').qtip({ - show: 'click', - hide: { - event: 'click unfocus' - }, - content: { - text: $('.hansen_dropdown-menu') - }, - position: { - my: 'top right', - at: 'bottom right', - target: $('.hansen_dropdown-link'), - adjust: { - x: 10 - } - }, - style: { - tip: { - corner: 'top right', - mimic: 'top center', - border: 1, - width: 10, - height: 6 - } - } - }); - }, - - _openSource: function(e) { - e.preventDefault(); - - var source = $(e.target).closest('.info').attr('data-source'); - - ga('send', 'event', 'SourceWindow', 'Open', source); - this.sourceWindow.show(source).addScroll(); - }, - - _openDropdown: function(e) { - e.preventDefault(); - }, - - _redrawCircle: function(e) { - e.preventDefault(); - - var dataset = $(e.target).attr('data-slug'), - subtitle = $(e.target).text(); - - var api = this.dropdown.qtip('api'); - - api.hide(); - - $('.hansen_dropdown-link').html(subtitle); - - if (dataset === 'countries_gain') { - this._drawCircle('forest_loss', 'comp', { iso: this.iso }); - } else { - this._drawCircle('forest_loss', 'bars', { iso: this.iso, dataset: dataset }); - } - }, - - _positionScroll: function() { - this.indepth_bar = 0; - - var h_min = $('.country_state').offset().top - 48, - h_max = $('.country_conventions').offset().top - 48; - - if (this.$('.country_indepth').length > 0) { - this.indepth_bar = 48; - - h_min -= 48; - h_max -= 48; - - if ($(window).scrollTop() > (h_min) && $(window).scrollTop() < h_max) { - this.$indepth.css({ - position: 'fixed', - top: 0 - }); - } else if ($(window).scrollTop() >= h_max) { - this.$indepth.css({ - position: 'absolute', - top: h_max - h_min - }); - } else { - this.$indepth.css({ - position: 'absolute', - top: 0 - }); - } - } - - if ($(window).scrollTop() > (h_min) && $(window).scrollTop() < h_max) { - this.$nav.css({ - position: 'fixed', - top: this.indepth_bar - }); - } else if ($(window).scrollTop() >= h_max) { - this.$nav.css({ - position: 'absolute', - top: h_max - h_min - }); - } else { - this.$nav.css({ - position: 'absolute', - top: 0 - }); - } - }, - - _stickynav: function() { - this._positionScroll(); - - $.scrollIt({ - upKey: null, - downKey: null, - easing: 'linear', - scrollTime: 600, - activeClass: 'active', - onPageChange: null, - topOffset: -48 - this.indepth_bar - }); - - $(window).scroll(this._positionScroll); - }, - - _drawCountry: function(iso, callback) { - var that = this; - - var sql = ['SELECT the_geom', - 'FROM forest_cov_glob_v3', - "WHERE country_code = '"+iso+"'", - 'UNION', - 'SELECT the_geom', - 'FROM ne_50m_admin_0_countries', - "WHERE adm0_a3 = '"+iso+"'&format=topojson"].join(' '); - - d3.json('https://wri-01.cartodb.com/api/v2/sql?q='+sql, function(error, topology) { - if (iso === 'TWN') { - draw(topology, 0, 'CHN', { alerts: true, bounds: that.bounds }); - } else { - var bounds = draw(topology, 0, iso, { alerts: true }); - - if (iso === 'CHN') that.bounds = bounds; - } - - callback && callback(); - }); - }, - - _drawForest: function(iso) { - var sql = ["SELECT unnest(array['forest_primary', 'forest_regenerated', 'forest_planted'])", - 'AS type, unnest(array[COALESCE(forest_primary, 0),', - 'COALESCE(forest_regenerated, 0),', - 'COALESCE(forest_planted, 0)])', - 'AS percent', - 'FROM gfw2_countries', - "WHERE iso = '"+iso+"'"].join(' '); - - d3.json('https://wri-01.cartodb.com/api/v2/sql?q='+sql, function(json) { - var data = json.rows; - - _.each(data, function(type) { - if (type['percent'] !== 0) { - $('.country_state-title').css({ - 'padding-top': '40px' - }); - $('.forest_type').show(); - } - }); - - var svg = d3.select('.country_state .line-graph') - .append('svg') - .attr('width', 635) - .attr('height', 50); - - var x_extent = [0, 100], - x_scale = d3.scale.linear() - .range([0, 590]) - .domain(x_extent); - - var origins = [], - aggr = 0, - klass = ['one', 'three', 'four'], - type = ['Primary', 'Regenerated', 'Planted']; - - _.each(data, function(d, i) { - var current = data[i-1] && data[i-1]['percent'] || 0; - - aggr += current; - - origins[i] = aggr; - }); - - // add lines - svg.selectAll('rect') - .data(data) - .enter() - .append('rect') - .attr('class', function(d, i) { - return 'line ' + klass[i]; - }) - .attr('x', function(d, i) { - return x_scale(origins[i]); - }) - .attr('y', 19) - .attr('width', function(d) { - return x_scale(d['percent']); - }) - .attr('height', 4) - .attr('rx', 2) - .attr('ry', 2); - - // add balls - svg.selectAll('circle') - .data(data) - .enter() - .append('svg:circle') - .attr('class', function(d, i) { - return klass[i]; - }) - .attr('cx', function(d, i) { - return x_scale(d['percent']+origins[i]); - }) - .attr('cy', 21) - .attr('r', 5) - .style('visibility', function(d) { - return d['percent'] === 0 ? 'hidden' : 'visible'; - }); - - // add values - svg.selectAll('.text') - .data(data) - .enter() - .append('text') - .text(function(d, i) { - return type[i]+' '+d['percent']+'%'; - }) - .attr('class', function(d, i) { - return 'text ' + klass[i]; - }) - .attr('x', function(d, i) { - var w_line = $('.line.'+klass[i]).attr('width'), - w_text = $(this).width(); - - if ((i === 0 && w_text > w_line) || - (i === 1 && w_text > (parseFloat(w_line) + - parseFloat($('.line.one').attr('width'))))) { - return x_scale(d['percent']+origins[i]) - w_line; - } else if (i === 2 && w_text > w_line) { - return x_scale(d['percent']+origins[i]) - w_text; - } else { - var offset = (w_text > w_line) ? w_text : (w_line/2 + w_text/2); - - return x_scale(d['percent']+origins[i]) - offset; - } - }) - .attr('y', function(d, i) { - return (i % 2 === 0) ? 42 : 08; - }) - .style('visibility', function(d) { - return d['percent'] === 0 ? 'hidden' : 'visible'; - }); - }); - }, - - _drawTenure: function(iso) { - var sql = ['SELECT tenure_government, tenure_owned, tenure_owned_individuals,', - 'tenure_reserved, GREATEST(tenure_government, tenure_owned,', - 'tenure_owned_individuals,', - 'tenure_owned_individuals,', - 'tenure_reserved) as max', - "FROM gfw2_countries WHERE iso = '"+iso+"'"].join(' '); - - d3.json('https://wri-01.cartodb.com/api/v2/sql?q='+sql, function(json) { - var data = json.rows[0], - h = 0; - - var x_extent = [0, data.max], - x_scale = d3.scale.linear() - .range([0, 500]) - .domain(x_extent); - - var origins = [], - aggr = 0, - klass = ['one', 'two', 'three', 'four'] - - var tenures = [ - { - name: 'Public lands administered by the government', - percent: data.tenure_government - }, - { - name: 'Public lands reserved for communities and indigenous groups', - percent: data.tenure_reserved - }, - { - name: 'Private lands owned by communities and indigenous groups', - percent: data.tenure_owned - }, - { - name: 'Private lands owned by firms and individuals', - percent: data.tenure_owned_individuals - } - ]; - - var tenures_ord = []; - - _.each(tenures, function(tenure, i) { - if (tenure['percent'] !== null && tenure['percent'] !== 0) { - if ($('.forest_tenure').not(':visible')) { - $('.forest_tenure').show(); - } - - h += 50; - - tenures_ord.push({ - name: tenure['name'], - percent: tenure['percent'] - }); - } - }); - - var svg = d3.select('.country_laws .line-graph') - .append('svg') - .attr('width', 570) - .attr('height', h); - - // add lines - svg.selectAll('rect') - .data(tenures_ord) - .enter() - .append('rect') - .attr('class', function(d, i) { - return klass[i]; - }) - .attr('x', function() { - return x_scale(0); - }) - .attr('y', function(d, i) { - return 25 + (50 * i); - }) - .attr('width', function(d) { - return x_scale(d['percent']); - }) - .attr('height', 4) - .attr('rx', 2) - .attr('ry', 2); - - // add balls - svg.selectAll('circle') - .data(tenures_ord) - .enter() - .append('svg:circle') - .attr('class', function(d, i) { - return klass[i]; - }) - .attr('cx', function(d, i) { - return x_scale(d['percent']); - }) - .attr('cy', function(d, i) { - return 27 + (50 * i); - }) - .attr('r', 5); - - // add values - svg.selectAll('.units') - .data(tenures_ord) - .enter() - .append('text') - .text(function(d) { - return d['percent']/1000000 + 'Mha'; - }) - .attr('class', function(d, i) { - return 'units ' + klass[i]; - }) - .attr('x', function(d, i) { - return x_scale(d['percent'])+10; - }) - .attr('y', function(d, i) { - return 31 + (50 * i); - }); - - // add legend - svg.selectAll('.legend') - .data(tenures_ord) - .enter() - .append('text') - .text(function(d) { - return d['name']; - }) - .attr('class', function(d, i) { - return 'legend ' + klass[i]; - }) - .attr('x', 0) - .attr('y', function(d, i) { - return 15 + (50 * i); - }); - }); - }, - - _drawCircle: function(id, type, options) { - var that = this; - - var $graph = $('.'+id), - $amount = $('.'+id+' .graph-amount'), - $date = $('.'+id+' .graph-date'), - $coming_soon = $('.'+id+' .coming_soon'), - $action = $('.'+id+' .action'); - - $('.graph.'+id+' .frame_bkg').empty(); - $graph.addClass('ghost'); - $amount.html(''); - $date.html(''); - $coming_soon.hide(); - - var width = options.width || 256, - height = options.height || width, - h = 100, // maxHeight - radius = width / 2; - - var graph = d3.select('.graph.'+id+' .frame_bkg') - .append('svg:svg') - .attr('class', type) - .attr('width', width) - .attr('height', height); - - var dashedLines = [ - { x1:17, y:height/4, x2:239, color: '#ccc' }, - { x1:0, y:height/2, x2:width, color: '#ccc' }, - { x1:17, y:3*height/4, x2:239, color: '#ccc' } - ]; - - // Adds the dotted lines - _.each(dashedLines, function(line) { - graph.append('svg:line') - .attr('x1', line.x1) - .attr('y1', line.y) - .attr('x2', line.x2) - .attr('y2', line.y) - .style('stroke-dasharray', '2,2') - .style('stroke', line.color); - }); - - var sql = ["SELECT date_trunc('month', date) as date, COUNT(*) as alerts", - 'FROM forma_api', - "WHERE iso = '"+options.iso+"'", - "GROUP BY date_trunc('month', date)", - "ORDER BY date_trunc('month', date) ASC"].join(' '); - - if (type === 'lines') { - d3.json('https://wri-01.cartodb.com/api/v2/sql?q='+sql, function(json) { - if (json && json.rows.length > 0) { - $graph.removeClass('ghost'); - $action.removeClass('disabled'); - that._initFormaDropdown(); - - var data = json.rows.slice(1, json.rows.length); - } else { - $coming_soon.show(); - - return; - } - - var x_scale = d3.scale.linear() - .domain([0, data.length - 1]) - .range([0, width - 80]); - - var max = d3.max(data, function(d) { return parseFloat(d.alerts); }); - - if (max === d3.min(data, function(d) { return parseFloat(d.alerts); })) { - h = h/2; - } - - var y_scale = d3.scale.linear() - .domain([0, max]) - .range([0, h]); - - var line = d3.svg.line() - .x(function(d, i) { return x_scale(i); }) - .y(function(d, i) { return h - y_scale(d.alerts); }) - .interpolate('basis'); - - var marginLeft = 40, - marginTop = radius - h/2; - - $amount.html(''+formatNumber(data[data.length - 1].alerts)+''); - - var date = new Date(data[data.length - 1].date), - form_date = 'Alerts in ' + config.MONTHNAMES[date.getUTCMonth()] + ' ' + date.getUTCFullYear(); - - $date.html(form_date); - - var cx = width - 80 + marginLeft; - var cy = h - y_scale(data[data.length - 1].alerts) + marginTop; - - graph.append('svg:path') - .attr('transform', 'translate(' + marginLeft + ',' + marginTop + ')') - .attr('d', line(data)) - .on('mousemove', function(d) { - var index = Math.round(x_scale.invert(d3.mouse(this)[0])); - - if (data[index]) { // if there's data - $amount.html(''+formatNumber(data[index].alerts)+''); - - var date = new Date(data[index].date), - form_date = 'Alerts in ' + config.MONTHNAMES[date.getUTCMonth()] + ' ' + date.getUTCFullYear(); - - $date.html(form_date); - - var cx = d3.mouse(this)[0] + marginLeft; - var cy = h - y_scale(data[index].alerts) + marginTop; - - graph.select('.forma_marker') - .attr('cx', cx) - .attr('cy', cy); - } - }); - - graph.append('svg:circle') - .attr('class', 'forma_marker') - .attr('cx', cx) - .attr('cy', cy) - .attr('r', 5); - }); - } else if (type === 'bars') { - var sql = "SELECT "; - - if (options.dataset === 'loss') { - sql += "year, loss_gt_0 loss FROM umd WHERE iso='"+options.iso+"'"; - } else if (options.dataset === 'extent') { - sql += "year, extent_gt_25 extent FROM umd WHERE iso='"+options.iso+"'"; - } - - d3.json('https://wri-01.cartodb.com/api/v2/sql?q='+sql, function(json) { - if (json) { - $graph.removeClass('ghost'); - - var data = json.rows; - } else { - $coming_soon.show(); - - return; - } - - var data_ = []; - - _.each(data, function(val, key) { - if (val.year >= 2001) { - data_.push({ - 'year': val.year, - 'value': eval('val.'+options.dataset) - }); - } - }); - - $amount.html(''+formatNumber(parseInt(data_[data_.length - 1].value, 10))+''); - $date.html('Hectares in ' + data_[data_.length - 1].year); - - var marginLeft = 40, - marginTop = radius - h/2 + 5; - - var y_scale = d3.scale.linear() - .domain([0, d3.max(data_, function(d) { return parseFloat(d.value); })]) - .range([height, marginTop*2]); - - var barWidth = (width - 80) / data_.length; - - var bar = graph.selectAll('g') - .data(data_) - .enter() - .append('g') - .attr('transform', function(d, i) { return 'translate(' + (marginLeft + i * barWidth) + ','+ -marginTop+')'; }); - - bar.append('svg:rect') - .attr('class', function(d, i) { - if (i === 11) { // last year index - return 'last bar' - } else { - return 'bar' - } - }) - .attr('y', function(d) { return y_scale(d.value); }) - .attr('height', function(d) { return height - y_scale(d.value); }) - .attr('width', barWidth - 1) - .on('mouseover', function(d) { - d3.selectAll('.bar').style('opacity', '.5'); - d3.select(this).style('opacity', '1'); - - $amount.html(''+formatNumber(parseInt(d.value, 10))+''); - $date.html('Hectares in ' + d.year); - }); - }); - } else if (type === 'comp') { - var sql = 'SELECT y2001_y2012 as gain, (SELECT SUM('; - - for(var y = 2001; y < 2012; y++) { - sql += 'y'+y+' + '; - } - - sql += ['y2012) FROM countries_loss', - "WHERE iso = '"+options.iso+"') as loss", - 'FROM countries_gain', - "WHERE iso = '"+options.iso+"'"].join(' '); - - d3.json('https://wri-01.cartodb.com/api/v2/sql?q='+encodeURIComponent(sql), function(json) { - if (json) { - $graph.removeClass('ghost'); - - var data = json.rows[0]; - } else { - $coming_soon.show(); - - return; - } - - var data_ = [], - form_key = { - 'gain': 'Tree cover gain', - 'loss': 'Tree cover loss' - }; - - _.each(data, function(val, key) { - data_.push({ - 'key': form_key[key], - 'value': val - }); - }); - - $amount.html(''+formatNumber(parseInt(data_[data_.length - 1].value, 10))+''); - $date.html('Ha '+data_[data_.length - 1].key); - - var barWidth = (width - 80) / 12; - - var marginLeft = 40 + barWidth*5, - marginTop = radius - h/2 + 5; - - var y_scale = d3.scale.linear() - .domain([0, d3.max(data_, function(d) { return parseFloat(d.value); })]) - .range([height, marginTop*2]); - - var bar = graph.selectAll('g') - .data(data_) - .enter() - .append('g') - .attr('transform', function(d, i) { return 'translate(' + (marginLeft + i * barWidth) + ',' + -marginTop + ')'; }); - - bar.append('svg:rect') - .attr('class', function(d, i) { - if (i === 1) { // last bar index - return 'last bar' - } else { - return 'bar' - } - }) - .attr('y', function(d) { return y_scale(d.value); }) - .attr('height', function(d) { return height - y_scale(d.value); }) - .attr('width', barWidth - 1) - .style('fill', '#FFC926') - .style('shape-rendering', 'crispEdges') - .on('mouseover', function(d) { - d3.selectAll('.bar').style('opacity', '.5'); - d3.select(this).style('opacity', '1'); - - $amount.html(''+formatNumber(parseFloat(d.value).toFixed(1))+''); - $date.html('Ha '+d.key); - }); - }); - } - } -}); - - -gfw.ui.view.CountriesIndex = cdb.core.View.extend({ - el: document.body, - - initialize: function() { - this._drawCountries(); - }, - - _drawCountries: function() { - var that = this; - - var sql = ['SELECT c.iso, c.enabled, m.the_geom', - 'FROM ne_50m_admin_0_countries m, gfw2_countries c', - 'WHERE c.iso = m.adm0_a3 AND c.enabled', - '&format=topojson'].join(' '); - - var sql_ = ['SELECT c.iso, m.the_geom', - 'FROM ne_50m_admin_0_countries m, gfw2_countries c', - 'WHERE c.iso = m.adm0_a3', - "AND c.iso = 'TWN'&format=topojson"].join(' '); - - d3.json('https://wri-01.cartodb.com/api/v2/sql?q='+sql, function(error, topology) { - for (var i = 0; i < Object.keys(topology.objects).length; i++) { - var iso = topology.objects[i].properties.iso; - - var bounds = draw(topology, i, iso, { alerts: false }); - - if (iso === 'CHN') { - that.bounds = bounds; - - d3.json('https://wri-01.cartodb.com/api/v2/sql?q='+sql_, function(error, topology) { - draw(topology, 0, 'CHN', { alerts: false, bounds: that.bounds}); - }); - } - } - }); - } -}); - - -gfw.ui.model.CountriesOverview = cdb.core.Model.extend({ - defaults: { - graph: 'total_loss', - years: true, - class: null - } -}); - - -gfw.ui.view.CountriesOverview = cdb.core.View.extend({ - el: document.body, - - events: { - 'click .info': '_openSource', - 'click .graph_tab': '_updateGraph', - 'click .countries_list__footer': '_drawList' - }, - - initialize: function() { - this.model = new gfw.ui.model.CountriesOverview(); - - this.$graph = $('.overview_graph__area'); - this.$years = $('.overview_graph__years'); - - var m = this.m = 40, - w = this.w = this.$graph.width()+(m*2), - h = this.h = this.$graph.height(), - vertical_m = this.vertical_m = 20; - - this.x_scale = d3.scale.linear() - .range([m, w-m]) - .domain([2001, 2012]); - - this.grid_scale = d3.scale.linear() - .range([vertical_m, h-vertical_m]) - .domain([0, 1]); - - this.model.bind('change:graph', this._redrawGraph, this); - this.model.bind('change:years', this._toggleYears, this); - this.model.bind('change:class', this._toggleClass, this); - - this._initViews(); - }, - - _initViews: function() { - this.sourceWindow = new gfw.ui.view.SourceWindow(); - this.$el.append(this.sourceWindow.render()); - - this.tooltip = d3.select('body') - .append('div') - .attr('class', 'tooltip'); - - this._drawYears(); - this._drawGraph(); - this._drawList(); - - Share = new gfw.ui.view.Share(); - this.$el.find('.overview_graph').append(Share.render()); - }, - - _openSource: function(e) { - e.preventDefault(); - - var source = $(e.target).closest('.info').attr('data-source'); - - ga('send', 'event', 'SourceWindow', 'Open', source); - this.sourceWindow.show(source).addScroll(); - }, - - _toggleYears: function() { - var that = this; - - if (this.model.get('years') === false) { - this.$years.slideUp(250, function() { - $('.overview_graph__axis').slideDown(); - }); - } else { - $('.overview_graph__axis').slideUp(250, function() { - that.$years.slideDown(); - }); - } - }, - - _showYears: function() { - if (!this.model.get('years')) { - this.model.set('years', true); - } - }, - - _hideYears: function() { - if (this.model.get('years')) { - this.model.set('years', false); - } - }, - - _updateGraph: function(e) { - e.preventDefault(); - - var $target = $(e.target).closest('.graph_tab'), - graph = $target.attr('data-slug'); - - if (graph === this.model.get('graph')) { - return; - } else { - $('.graph_tab').removeClass('selected'); - $target.addClass('selected'); - - this.model.set('graph', graph); - } - }, - - _redrawGraph: function() { - var graph = this.model.get('graph'); - - $('.overview_graph__title').html(config.GRAPHS[graph].title); - $('.overview_graph__legend p').html(config.GRAPHS[graph].subtitle); - $('.overview_graph__legend .info').attr('data-source', graph); - - this.$graph.find('.'+graph); - - this.$graph.find('.chart').hide(); - this.$graph.find('.'+graph).fadeIn(); - - this._drawGraph(); - this._drawList(); - }, - - _drawList: function(e) { - var that = this; - - e && e.preventDefault(); - - if (this.model.get('graph') === 'total_loss') { - var sql = 'WITH loss as (SELECT iso, SUM('; - - for(var y = 2001; y < 2012; y++) { - sql += 'y'+y+' + '; - } - - sql += 'y2012) as sum_loss\ - FROM countries_loss\ - GROUP BY iso)'; - - sql += 'SELECT c.iso, c.name, c.enabled, sum_loss\ - FROM loss, gfw2_countries c\ - WHERE loss.iso = c.iso\ - AND NOT sum_loss = 0\ - ORDER BY sum_loss DESC '; - - if (e) { - sql += 'OFFSET 10'; - } else { - sql += 'LIMIT 10'; - } - - d3.json('http://wri-01.cartodb.com/api/v2/sql/?q='+encodeURIComponent(sql), function(json) { - var self = that, - markup_list = ''; - - var data = json.rows; - - _.each(data, function(val, key) { - var ord = e ? (key+11) : (key+1), - enabled = val.enabled ? ''+val.name+'' : val.name; - - markup_list += '
  • \ -
    \ -
    '+ord+'
    \ -
    '+enabled+'
    \ -
  • '; - }); - - if (e) { - $('.countries_list__footer').fadeOut(); - } else { - $('.countries_list ul').html(''); - $('.countries_list__footer').show(); - - $('.countries_list__header__minioverview').html('Loss vs Gain'); - } - - $('.countries_list ul').append(markup_list); - - that.model.set('class', null); - - _.each(data, function(val, key) { - self._drawMiniOverview(val.iso); - }); - }); - } else if (this.model.get('graph') === 'percent_loss') { - var sql = 'SELECT c.iso, c.name, c.enabled, loss_y2001_y2012 as ratio_loss\ - FROM countries_percent percent, gfw2_countries c\ - WHERE percent.iso = c.iso AND c.enabled IS true\ - AND NOT loss_y2001_y2012 = 0\ - ORDER BY ratio_loss DESC '; - - if (e) { - sql += 'OFFSET 10'; - } else { - sql += 'LIMIT 10'; - } - - d3.json('http://wri-01.cartodb.com/api/v2/sql/?q='+encodeURIComponent(sql), function(json) { - var self = that, - markup_list = ''; - - var data = json.rows; - - _.each(data, function(val, key) { - var ord = e ? (key+11) : (key+1), - enabled = val.enabled ? ''+val.name+'' : val.name; - - markup_list += '
  • \ -
    \ -
    '+ord+'
    \ -
    '+enabled+'
    \ -
  • '; - }); - - if (e) { - $('.countries_list__footer').fadeOut(); - } else { - $('.countries_list ul').html(''); - $('.countries_list__footer').show(); - - $('.countries_list__header__minioverview').html('% Loss'); - } - - $('.countries_list ul').append(markup_list); - - that.model.set('class', null); - - _.each(data, function(val, key) { - self._drawMiniOverview(val.iso); - }); - }); - } else if (this.model.get('graph') === 'total_extent') { - var sql = 'WITH extent as (SELECT iso, SUM('; - - for(var y = 2001; y < 2012; y++) { - sql += 'y'+y+' + '; - } - - sql += 'y2012) as sum_extent\ - FROM extent_gt_25\ - GROUP BY iso)'; - - sql += 'SELECT c.iso, c.name, c.enabled, sum_extent\ - FROM extent, gfw2_countries c\ - WHERE extent.iso = c.iso\ - AND NOT sum_extent = 0\ - ORDER BY sum_extent DESC '; - - if (e) { - sql += 'OFFSET 10'; - } else { - sql += 'LIMIT 10'; - } - - d3.json('http://wri-01.cartodb.com/api/v2/sql/?q='+encodeURIComponent(sql), function(json) { - var self = that, - markup_list = ''; - - var data = json.rows; - - _.each(data, function(val, key) { - var ord = e ? (key+11) : (key+1), - enabled = val.enabled ? ''+val.name+'' : val.name; - - markup_list += '
  • \ -
    \ -
    '+ord+'
    \ -
    '+enabled+'
    \ -
  • '; - }); - - if (e) { - $('.countries_list__footer').fadeOut(); - } else { - $('.countries_list ul').html(''); - $('.countries_list__footer').show(); - - $('.countries_list__header__minioverview').html('Cover extent vs Cover loss'); - } - - $('.countries_list ul').append(markup_list); - - that.model.set('class', 'expanded'); - - _.each(data, function(val, key) { - self._drawMiniOverview(val.iso); - }); - }); - } else if (this.model.get('graph') === 'ratio') { - var sql = 'WITH loss as (SELECT iso, SUM('; - - for(var y = 2001; y < 2012; y++) { - sql += 'loss.y'+y+' + '; - } - - sql += 'loss.y2012) as sum_loss\ - FROM loss_gt_50 loss\ - GROUP BY iso), gain as (SELECT g.iso, SUM(y2001_y2012) as sum_gain\ - FROM countries_gain g, loss_gt_50 loss\ - WHERE loss.iso = g.iso\ - GROUP BY g.iso), ratio as ('; - - sql += 'SELECT c.iso, c.name, c.enabled, loss.sum_loss/gain.sum_gain as ratio\ - FROM loss, gain, gfw2_countries c\ - WHERE sum_gain IS NOT null\ - AND NOT sum_gain = 0\ - AND c.iso = gain.iso\ - AND c.iso = loss.iso\ - ORDER BY loss.sum_loss DESC\ - LIMIT 50) '; - - sql += 'SELECT *\ - FROM ratio\ - WHERE ratio IS NOT null\ - ORDER BY ratio DESC '; - - if (e) { - sql += ['OFFSET 10', - 'LIMIT 40'].join('\n'); - } else { - sql += 'LIMIT 10'; - } - - d3.json('http://wri-01.cartodb.com/api/v2/sql/?q='+encodeURIComponent(sql), function(json) { - var self = that, - markup_list = ''; - - var data = json.rows; - - _.each(data, function(val, key) { - var ord = e ? (key+11) : (key+1), - enabled = val.enabled ? ''+val.name+'' : val.name; - - markup_list += '
  • \ -
    '+formatNumber(parseFloat(val.ratio).toFixed(2))+'
    \ -
    '+ord+'
    \ -
    '+enabled+'
    \ -
  • '; - }); - - if (e) { - $('.countries_list__footer').fadeOut(); - } else { - $('.countries_list ul').html(''); - $('.countries_list__footer').show(); - - $('.countries_list__header__minioverview').html('Ratio of Loss to Gain'); - } - - $('.countries_list ul').append(markup_list); - - that.model.set('class', 'medium'); - - _.each(data, function(val, key) { - self._drawMiniOverview(val.iso); - }); - }); - } else if (this.model.get('graph') === 'domains') { - var sql = 'SELECT name, total_loss, total_gain, GREATEST(' - - for(var y = 2001; y < 2012; y++) { - sql += 'y'+y+', ' - } - - sql += 'y2012) as max\ - FROM countries_domains\ - ORDER BY total_loss DESC '; - - d3.json('http://wri-01.cartodb.com/api/v2/sql/?q='+encodeURIComponent(sql), function(json) { - var self = that, - markup_list = ''; - - var data = json.rows; - - _.each(data, function(val, key) { - markup_list += ['
  • ', - '
    ', - '
    '+formatNumber(parseFloat(val.total_loss/1000000).toFixed(1))+' Mha
    ', - '
    '+formatNumber(parseFloat(val.total_gain/1000000).toFixed(1))+' Mha
    ', - '
    ', - '
    '+(key+1)+'
    ', - '
    '+val.name+'
    ', - '
  • '].join(''); - }); - - $('.countries_list__footer').hide(); - $('.countries_list__header__minioverview').html('Total loss vs Total gain'); - $('.countries_list ul').html(markup_list); - - that.model.set('class', 'huge'); - - _.each(data, function(val, key) { - self._drawMiniOverview(val.iso); - }); - }); - } - }, - - _toggleClass: function() { - if (this.model.get('class') === 'expanded') { - $('.countries_list__header__minioverview').addClass('expanded'); - $('.countries_list__minioverview').addClass('expanded'); - - $('.countries_list__header__minioverview').removeClass('medium huge'); - $('.countries_list__minioverview').removeClass('medium huge'); - } else if (this.model.get('class') === 'medium') { - $('.countries_list__header__minioverview').addClass('medium'); - $('.countries_list__minioverview').addClass('medium'); - - $('.countries_list__header__minioverview').removeClass('expanded huge'); - $('.countries_list__minioverview').removeClass('expanded huge'); - } else if (this.model.get('class') === 'huge') { - $('.countries_list__header__minioverview').addClass('huge'); - $('.countries_list__minioverview').addClass('huge'); - - $('.countries_list__header__minioverview').removeClass('expanded medium'); - $('.countries_list__minioverview').removeClass('expanded medium'); - } else { - $('.countries_list__header__minioverview').removeClass('expanded medium huge'); - $('.countries_list__minioverview').removeClass('expanded medium huge'); - } - }, - - _drawMiniOverview: function(iso) { - var width = 90, - height = 30; - - var graph = d3.select('.countries_list__minioverview_'+iso) - .append('svg:svg') - .attr('width', width) - .attr('height', height); - - if (this.model.get('graph') === ('total_loss')) { - var sql = 'SELECT '; - - for(var y = 2001; y < 2012; y++) { - sql += 'y'+y+', ' - } - - sql += "y2012, (SELECT y2001_y2012\ - FROM countries_gain\ - WHERE c.iso = iso) as gain\ - FROM loss_gt_0 c \ - WHERE iso = '"+iso+"'"; - - d3.json('https://wri-01.cartodb.com/api/v2/sql?q='+sql, function(json) { - var data = json.rows[0]; - - var data_ = [], - gain = null; - - _.each(data, function(val, key) { - if (key === 'gain') { - gain = val/12; - } else { - data_.push({ - 'year': key.replace('y',''), - 'value': val - }); - } - }); - - var y_scale = d3.scale.linear() - .domain([0, d3.max(data_, function(d) { return d.value; })]) - .range([height, 0]); - - var barWidth = width / data_.length; - - var bar = graph.selectAll('g') - .data(data_) - .enter().append('g') - .attr('transform', function(d, i) { return 'translate(' + (i * barWidth) + ', 0)'; }); - - bar.append('svg:rect') - .attr('class', 'bar') - .attr('y', function(d) { return y_scale(d.value); }) - .attr('height', function(d) { return height - y_scale(d.value); }) - .attr('width', barWidth - 1); - - var data_gain_ = [ - { - year: 2001, - value: gain - }, - { - year: 2012, - value: gain - } - ]; - - graph.selectAll('line.minioverview_line') - .data(data_gain_) - .enter() - .append('line') - .attr({ - 'class': 'minioverview_line', - 'x1': 0, - 'x2': width, - 'y1': function(d) { return y_scale(gain); }, - 'y2': function(d) { return y_scale(gain); } - }); - }); - } else if (this.model.get('graph') === ('percent_loss')) { - var sql = 'WITH loss as (SELECT '; - - for(var y = 2001; y < 2012; y++) { - sql += 'y'+y+' as loss_y'+y+', '; - } - - sql += "y2012 as loss_y2012\ - FROM loss_gt_25\ - WHERE iso = '"+iso+"'), extent as (SELECT "; - - for(var y = 2001; y < 2012; y++) { - sql += 'y'+y+' as extent_y'+y+', '; - } - - sql += "y2012 as extent_y2012\ - FROM extent_gt_25\ - WHERE iso = '"+iso+"')"; - - sql += 'SELECT '; - - for(var y = 2001; y < 2012; y++) { - sql += 'loss_y'+y+'/extent_y'+y+' as percent_'+y+', '; - } - - sql += 'loss_y2012/extent_y2012 as percent_2012\ - FROM loss, extent'; - - d3.json('https://wri-01.cartodb.com/api/v2/sql?q='+encodeURIComponent(sql), function(json) { - var data = json.rows[0]; - - var data_ = []; - - _.each(data, function(val, key) { - data_.push({ - 'year': key.replace('y',''), - 'value': val*100 - }); - }); - - var y_scale = d3.scale.linear() - .domain([0, d3.max(data_, function(d) { return d.value; })]) - .range([height, 0]); - - var barWidth = width / data_.length; - - var bar = graph.selectAll('g') - .data(data_) - .enter().append('g') - .attr('transform', function(d, i) { return 'translate(' + (i * barWidth) + ', 0)'; }); - - bar.append('svg:rect') - .attr('class', 'bar') - .attr('y', function(d) { return y_scale(d.value); }) - .attr('height', function(d) { return height - y_scale(d.value); }) - .attr('width', barWidth - 1); - - }); - } else if (this.model.get('graph') === ('total_extent')) { - var sql = 'SELECT '; - - for(var y = 2001; y < 2012; y++) { - sql += 'loss.y'+y+' as loss_y'+y+', '; - } - - sql += 'loss.y2012 as loss_y2012, '; - - for(var y = 2001; y < 2012; y++) { - sql += 'extent.y'+y+' as extent_y'+y+', '; - } - - sql += "extent.y2012 as extent_y2012\ - FROM loss_gt_0 loss, extent_gt_25 extent\ - WHERE loss.iso = extent.iso\ - AND loss.iso = '"+iso+"'"; - - d3.json('https://wri-01.cartodb.com/api/v2/sql?q='+sql, function(json) { - - var graph2 = d3.select('.countries_list__minioverview_'+iso) - .append('div') - .attr('class', 'sibling') - .append('svg:svg') - .attr('width', width) - .attr('height', height); - - var data = json.rows[0]; - - var data_loss_ = [], - data_extent_ = []; - - _.each(data, function(val, key) { - if (key.indexOf('loss_y') != -1) { - data_loss_.push({ - 'year': key.split('_y')[1], - 'value': val - }); - } - - if (key.indexOf('extent_y') != -1) { - data_extent_.push({ - 'year': key.split('extent_y')[1], - 'value': val - }); - } - }); - - var y_scale_loss = d3.scale.linear() - .domain([0, d3.max(data_loss_, function(d) { return d.value; })]) - .range([height, 0]); - - var y_scale_extent = d3.scale.linear() - .domain([0, d3.max(data_extent_, function(d) { return d.value; })]) - .range([height, 0]); - - var barWidth_loss = width / data_loss_.length; - - var bar = graph.selectAll('g') - .data(data_loss_) - .enter() - .append('g') - .attr('transform', function(d, i) { return 'translate(' + (i * barWidth_loss) + ', 0)'; }); - - bar.append('svg:rect') - .attr('class', 'bar') - .attr('y', function(d) { return y_scale_loss(d.value); }) - .attr('height', function(d) { return height - y_scale_loss(d.value); }) - .attr('width', barWidth_loss - 1); - - var barWidth_extent = width / data_extent_.length; - - var bar2 = graph2.selectAll('g') - .data(data_extent_) - .enter() - .append('g') - .attr('transform', function(d, i) { return 'translate(' + (i * barWidth_extent) + ', 0)'; }); - - bar2.append('svg:rect') - .attr('class', 'bar extent') - .attr('y', function(d) { return y_scale_extent(d.value); }) - .attr('height', function(d) { return height - y_scale_extent(d.value); }) - .attr('width', barWidth_extent - 1); - }); - } - }, - - _drawYears: function() { - var markup_years = ''; - - for (var y = 2001; y<=2012; y += 1) { - var y_ = this.x_scale(y); - - if (y === 2001) { - y_ -= 25; - } else if (y === 2012) { - y_ -= 55; - } else { - y_ -= 40; - } - - markup_years += ''+y+''; - } - - this.$years.html(markup_years); - }, - - _drawGraph: function() { - var that = this; - - var w = this.w, - h = this.h, - vertical_m = this.vertical_m, - m = this.m, - x_scale = this.x_scale; - - var grid_scale = d3.scale.linear() - .range([vertical_m, h-vertical_m]) - .domain([1, 0]); - - d3.select('#chart').remove(); - - var svg = d3.select('.overview_graph__area') - .append('svg:svg') - .attr('id', 'chart') - .attr('width', w) - .attr('height', h); - - // grid - svg.selectAll('line.grid_h') - .data(grid_scale.ticks(4)) - .enter() - .append('line') - .attr({ - 'class': 'grid grid_h', - 'x1': 0, - 'x2': w, - 'y1': function(d, i) { return grid_scale(d); }, - 'y2': function(d, i) { return grid_scale(d); } - }); - - svg.selectAll('line.grid_v') - .data(x_scale.ticks(12)) - .enter() - .append('line') - .attr({ - 'class': 'grid grid_v', - 'y1': h, - 'y2': 0, - 'x1': function(d) { return x_scale(d); }, - 'x2': function(d) { return x_scale(d); } - }); - - var gradient = svg.append('svg:defs') - .append('svg:linearGradient') - .attr('id', 'gradient') - .attr('x1', '0%') - .attr('y1', '0%') - .attr('x2', '0%') - .attr('y2', '100%') - .attr('spreadMethod', 'pad'); - - gradient.append('svg:stop') - .attr('offset', '0%') - .attr('stop-color', '#CA46FF') - .attr('stop-opacity', .5); - - gradient.append('svg:stop') - .attr('offset', '100%') - .attr('stop-color', '#D24DFF') - .attr('stop-opacity', 1); - - if (this.model.get('graph') === 'total_loss') { - this._showYears(); - - svg.append('text') - .attr('class', 'axis') - .attr('id', 'axis_y') - .text('Mha') - .attr('x', -h/2) - .attr('y', 30) - .attr('transform', 'rotate(-90)'); - - var sql = 'SELECT '; - - for(var y = 2001; y < 2012; y++) { - sql += 'SUM(y'+y+') as y'+y+', ' - } - - sql += 'SUM(y2012) as y2012, (SELECT SUM(y2001_y2012)\ - FROM countries_gain) as gain\ - FROM loss_gt_0'; - - d3.json('https://wri-01.cartodb.com/api/v2/sql?q='+sql, function(error, json) { - var data = json.rows[0]; - - var data_ = [], - gain = null; - - _.each(data, function(val, key) { - if (key === 'gain') { - gain = val/12; - } else { - data_.push({ - 'year': key.replace('y',''), - 'value': val - }); - } - }); - - var y_scale = d3.scale.linear() - .range([vertical_m, h-vertical_m]) - .domain([d3.max(data_, function(d) { return d.value; }), 0]); - - // area - var area = d3.svg.area() - .x(function(d) { return x_scale(d.year); }) - .y0(h) - .y1(function(d) { return y_scale(d.value); }); - - svg.append('path') - .datum(data_) - .attr('class', 'area') - .attr('d', area) - .style('fill', 'url(#gradient)'); - - // circles - svg.selectAll('circle') - .data(data_) - .enter() - .append('svg:circle') - .attr('class', 'linedot') - .attr('cx', function(d) { - return x_scale(d.year); - }) - .attr('cy', function(d){ - return y_scale(d.value); - }) - .attr('r', 6) - .attr('name', function(d) { - return ''+d.year+''+formatNumber(parseFloat(d.value/1000000).toFixed(1))+' Mha'; - }) - .on('mouseover', function(d) { - that.tooltip.html($(this).attr('name')) - .style('visibility', 'visible') - .style('top', $(this).offset().top-100+'px') - .style('left', $(this).offset().left-$('.tooltip').width()/2-4+'px') - .attr('class', 'tooltip'); - - d3.select(this) - .transition() - .duration(100) - .attr('r', 7); - - // TODO: highlighting the legend - }) - .on('mouseout', function(d) { - that.tooltip.style('visibility', 'hidden'); - - d3.select(this) - .transition() - .duration(100) - .attr('r', 6); - - // TODO: highlighting the legend - }); - - var data_gain_ = [ - { - year: 2001, - value: gain - }, - { - year: 2012, - value: gain - } - ]; - - // line - svg.selectAll('line.overview_line') - .data([data_gain_[0]]) - .enter() - .append('line') - .attr({ - 'class': 'overview_line', - 'x1': m, - 'x2': w-m, - 'y1': function(d) { return y_scale(gain); }, - 'y2': function(d) { return y_scale(gain); } - }); - - svg.selectAll('circle.gain') - .data(data_gain_) - .enter() - .append('svg:circle') - .attr('class', 'linedot gain') - .attr('cx', function(d) { - return x_scale(d.year); - }) - .attr('cy', function(d){ - return y_scale(d.value); - }) - .attr('r', 6) - .attr('name', function(d) { - return '2001-2012'+formatNumber(parseFloat(d.value/1000000).toFixed(1))+' Mha'; - }) - .on('mouseover', function(d) { - that.tooltip.html($(this).attr('name')) - .style('visibility', 'visible') - .style('top', $(this).offset().top-100+'px') - .style('left', $(this).offset().left-$('.tooltip').width()/2-4+'px') - .attr('class', 'tooltip gain_tooltip'); - - d3.select(this) - .transition() - .duration(100) - .attr('r', 7); - - // TODO: highlighting the legend - }) - .on('mouseout', function(d) { - that.tooltip.style('visibility', 'hidden'); - - d3.select(this) - .transition() - .duration(100) - .attr('r', 6); - - // TODO: highlighting the legend - }); - }); - } else if (this.model.get('graph') === 'percent_loss') { - this._showYears(); - - svg.append('text') - .attr('class', 'axis') - .attr('id', 'axis_y') - .text('%') - .attr('x', -h/2) - .attr('y', 30) - .attr('transform', 'rotate(-90)'); - - var sql = 'WITH loss as (SELECT '; - - for(var y = 2001; y < 2012; y++) { - sql += 'SUM(y'+y+') as sum_loss_y'+y+', '; - } - - sql += 'SUM(y2012) as sum_loss_y2012\ - FROM loss_gt_25), extent as (SELECT '; - - for(var y = 2001; y < 2012; y++) { - sql += 'SUM(y'+y+') as sum_extent_y'+y+', '; - } - - sql += 'SUM(y2012) as sum_extent_y2012\ - FROM extent_gt_25)\ - SELECT '; - - for(var y = 2001; y < 2012; y++) { - sql += 'sum_loss_y'+y+'/sum_extent_y'+y+' as percent_loss_'+y+', '; - } - - sql += 'sum_loss_y2012/sum_extent_y2012 as percent_loss_2012, (SELECT SUM(y2001_y2012)/('; - - for(var y = 2001; y < 2012; y++) { - sql += 'sum_extent_y'+y+' + '; - } - - sql += 'sum_extent_y2012)\ - FROM countries_gain) as gain\ - FROM loss, extent'; - - d3.json('https://wri-01.cartodb.com/api/v2/sql?q='+encodeURIComponent(sql), function(json) { - var data = json.rows[0]; - - var data_ = [], - gain = null; - - _.each(data, function(val, key) { - if (key === 'gain') { - gain = val/12; - } else { - data_.push({ - 'year': key.replace('percent_loss_',''), - 'value': val - }); - } - }); - - var y_scale = grid_scale; - - // area - var area = d3.svg.area() - .x(function(d) { return x_scale(d.year); }) - .y0(h) - .y1(function(d) { return y_scale(d.value*100); }); - - svg.append('path') - .datum(data_) - .attr('class', 'area') - .attr('d', area) - .style('fill', 'url(#gradient)'); - - // circles - svg.selectAll('circle') - .data(data_) - .enter() - .append('svg:circle') - .attr('class', 'linedot') - .attr('cx', function(d) { - return x_scale(d.year); - }) - .attr('cy', function(d){ - return y_scale(d.value*100); - }) - .attr('r', 6) - .attr('name', function(d) { - return ''+d.year+''+parseFloat(d.value*100).toFixed(2)+' %'; - }) - .on('mouseover', function(d) { - that.tooltip.html($(this).attr('name')) - .style('visibility', 'visible') - .style('top', $(this).offset().top-100+'px') - .style('left', $(this).offset().left-$('.tooltip').width()/2-4+'px') - .attr('class', 'tooltip'); - - d3.select(this) - .transition() - .duration(100) - .attr('r', 7); - - // TODO: highlighting the legend - }) - .on('mouseout', function(d) { - that.tooltip.style('visibility', 'hidden'); - - d3.select(this) - .transition() - .duration(100) - .attr('r', 6); - - // TODO: highlighting the legend - }); - - var data_gain_ = [ - { - year: 2001, - value: gain - }, - { - year: 2012, - value: gain - } - ]; - - // line - svg.selectAll('line.overview_line') - .data([data_gain_[0]]) - .enter() - .append('line') - .attr({ - 'class': 'overview_line', - 'x1': m, - 'x2': w-m, - 'y1': function(d) { return y_scale(gain*100); }, - 'y2': function(d) { return y_scale(gain*100); } - }); - - // circles - svg.selectAll('circle.gain') - .data(data_gain_) - .enter() - .append('svg:circle') - .attr('class', 'linedot gain') - .attr('cx', function(d) { - return x_scale(d.year); - }) - .attr('cy', function(d){ - return y_scale(d.value*100); - }) - .attr('r', 6) - .attr('name', function(d) { - return '2001-2012'+parseFloat(d.value*100).toFixed(2)+' %'; - }) - .on('mouseover', function(d) { - that.tooltip.html($(this).attr('name')) - .style('visibility', 'visible') - .style('top', $(this).offset().top-100+'px') - .style('left', $(this).offset().left-$('.tooltip').width()/2-4+'px') - .attr('class', 'tooltip gain_tooltip'); - - d3.select(this) - .transition() - .duration(100) - .attr('r', 7); - - // TODO: highlighting the legend - }) - .on('mouseout', function(d) { - that.tooltip.style('visibility', 'hidden'); - - d3.select(this) - .transition() - .duration(100) - .attr('r', 6); - - // TODO: highlighting the legend - }); - }); - } else if (this.model.get('graph') === 'total_extent') { - this._showYears(); - - svg.append('text') - .attr('class', 'axis') - .attr('id', 'axis_y') - .text('Mha') - .attr('x', -h/2) - .attr('y', 30) - .attr('transform', 'rotate(-90)'); - - var gradient_extent = svg.append('svg:defs') - .append('svg:linearGradient') - .attr('id', 'gradient_extent') - .attr('x1', '0%') - .attr('y1', '0%') - .attr('x2', '0%') - .attr('y2', '100%') - .attr('spreadMethod', 'pad'); - - gradient_extent.append('svg:stop') - .attr('offset', '0%') - .attr('stop-color', '#98BD17') - .attr('stop-opacity', .5); - - gradient_extent.append('svg:stop') - .attr('offset', '100%') - .attr('stop-color', '#98BD17') - .attr('stop-opacity', 1); - - var sql = 'SELECT '; - - for(var y = 2001; y < 2012; y++) { - sql += 'SUM(loss.y'+y+') as loss_y'+y+', '; - } - - sql += 'SUM(loss.y2012) as loss_y2012, '; - - for(var y = 2001; y < 2012; y++) { - sql += 'SUM(extent.y'+y+') as extent_y'+y+', '; - } - - sql += 'SUM(extent.y2012) as extent_y2012\ - FROM loss_gt_25 loss, extent_gt_25 extent\ - WHERE loss.iso = extent.iso'; - - d3.json('https://wri-01.cartodb.com/api/v2/sql?q='+encodeURIComponent(sql), function(json) { - var data = json.rows[0]; - - var data_ = [], - data_loss_ = [], - data_extent_ = []; - - _.each(data, function(val, key) { - var year = key.split('_y')[1]; - - var obj = _.find(data_, function(obj) { return obj.year == year; }); - - if (obj === undefined) { - data_.push({ 'year': year }); - } - - if (key.indexOf('loss_y') != -1) { - data_loss_.push({ - 'year': key.split('_y')[1], - 'value': val - }); - } - - if (key.indexOf('extent_y') != -1) { - data_extent_.push({ - 'year': key.split('extent_y')[1], - 'value': val - }); - } - }); - - _.each(data_, function(val) { - var loss = _.find(data_loss_, function(obj) { return obj.year == val.year; }), - extent = _.find(data_extent_, function(obj) { return obj.year == val.year; }); - - _.extend(val, { 'loss': loss.value, 'extent': extent.value }); - }); - - var domain = [d3.max(data_, function(d) { return d.extent; }), 0]; - - var y_scale = d3.scale.linear() - .range([vertical_m, h-vertical_m]) - .domain(domain); - - // area - var area_loss = d3.svg.area() - .x(function(d) { return x_scale(d.year); }) - .y0(h) - .y1(function(d) { return y_scale(d.loss); }); - - var area_extent = d3.svg.area() - .x(function(d) { return x_scale(d.year); }) - .y0(function(d) { return y_scale(d.extent); }) - .y1(function(d) { return y_scale(d.loss); }); - - svg.append('path') - .datum(data_) - .attr('class', 'area') - .attr('d', area_loss) - .style('fill', 'url(#gradient)'); - - svg.append('path') - .datum(data_) - .attr('class', 'area') - .attr('d', area_extent) - .style('fill', 'url(#gradient_extent)'); - - // circles - svg.selectAll('circle') - .data(data_loss_) - .enter() - .append('svg:circle') - .attr('class', 'linedot') - .attr('cx', function(d) { - return x_scale(d.year); - }) - .attr('cy', function(d){ - return y_scale(d.value); - }) - .attr('r', 6) - .attr('name', function(d) { - return ''+d.year+''+formatNumber(parseFloat(d.value/1000000).toFixed(1))+' Mha'; - }) - .on('mouseover', function(d) { - that.tooltip.html($(this).attr('name')) - .style('visibility', 'visible') - .style('top', $(this).offset().top-100+'px') - .style('left', $(this).offset().left-$('.tooltip').width()/2-4+'px') - .attr('class', 'tooltip'); - - d3.select(this) - .transition() - .duration(100) - .attr('r', 7); - - // TODO: highlighting the legend - }) - .on('mouseout', function(d) { - that.tooltip.style('visibility', 'hidden'); - - d3.select(this) - .transition() - .duration(100) - .attr('r', 6); - - // TODO: highlighting the legend - }); - - svg.selectAll('circle.gain') - .data(data_extent_) - .enter() - .append('svg:circle') - .attr('class', 'linedot gain') - .attr('cx', function(d) { - return x_scale(d.year); - }) - .attr('cy', function(d){ - return y_scale(d.value); - }) - .attr('r', 6) - .attr('name', function(d) { - return ''+d.year+''+formatNumber(parseFloat(d.value/1000000).toFixed(1))+' Mha'; - }) - .on('mouseover', function(d) { - that.tooltip.html($(this).attr('name')) - .style('visibility', 'visible') - .style('top', $(this).offset().top-100+'px') - .style('left', $(this).offset().left-$('.tooltip').width()/2-4+'px') - .attr('class', 'tooltip gain_tooltip'); - - d3.select(this) - .transition() - .duration(100) - .attr('r', 7); - - // TODO: highlighting the legend - }) - .on('mouseout', function(d) { - that.tooltip.style('visibility', 'hidden'); - - d3.select(this) - .transition() - .duration(100) - .attr('r', 6); - - // TODO: highlighting the legend - }); - }); - } else if (this.model.get('graph') === 'ratio') { - this._hideYears(); - - svg.append('text') - .attr('class', 'axis light') - .attr('id', 'axis_y') - .text('Ratio of tree cover loss to gain 2001-2012') - .attr('x', -(h/2)-60) - .attr('y', 30) - .attr('transform', 'rotate(-90)'); - - svg.append('text') - .attr('class', 'axis light') - .attr('id', 'axis_ratio') - .text('1') - .attr('x', 25) - .attr('y', h-60); - - var shadow = svg.append('svg:defs') - .append('svg:filter') - .attr('id', 'shadow') - .attr('x', '0%') - .attr('y', '0%') - .attr('width', '200%') - .attr('height', '200%'); - - shadow.append('svg:feOffset') - .attr('result', 'offOut') - .attr('in', 'SourceAlpha') - .attr('dx', 0) - .attr('dy', 0); - - shadow.append('svg:feGaussianBlur') - .attr('result', 'blurOut') - .attr('in', 'offOut') - .attr('stdDeviation', 1); - - shadow.append('svg:feBlend') - .attr('in', 'SourceGraphic') - .attr('in2', 'blurOut') - .attr('mode', 'normal'); - - var sql = 'WITH loss as (SELECT iso, SUM('; - - for(var y = 2001; y < 2012; y++) { - sql += 'loss.y'+y+' + '; - } - - sql += ['loss.y2012) as sum_loss', - 'FROM loss_gt_50 loss', - 'GROUP BY iso), gain as ('].join(' '); - - sql += ['SELECT g.iso, SUM(y2001_y2012) as sum_gain', - 'FROM countries_gain g, loss_gt_50 loss', - 'WHERE loss.iso = g.iso', - 'GROUP BY g.iso), ratio as ('].join(' '); - - sql += ['SELECT c.iso, c.name, c.enabled, loss.sum_loss as loss,', - 'gain.sum_gain as gain, loss.sum_loss/gain.sum_gain as ratio', - 'FROM loss, gain, gfw2_countries c', - 'WHERE sum_gain IS NOT null', - 'AND NOT sum_gain = 0', - 'AND c.iso = gain.iso', - 'AND c.iso = loss.iso', - 'ORDER BY loss.sum_loss DESC', - 'LIMIT 50), extent as ('].join(' '); - - sql += ['SELECT extent.iso, SUM(extent.y2012) as extent', - 'FROM countries_extent extent', - 'GROUP BY extent.iso) '].join(' '); - - sql += ['SELECT *', - 'FROM ratio, extent', - 'WHERE ratio IS NOT null', - 'AND extent.iso = ratio.iso'].join(' '); - - d3.json('https://wri-01.cartodb.com/api/v2/sql?q='+encodeURIComponent(sql), function(json) { - var data = json.rows; - - var log_m = 50; - - var x_log_scale = d3.scale.log() - .range([m, w-m]) - .domain([d3.min(data, function(d) { return d.extent; }), d3.max(data, function(d) { return d.extent; })]); - - var y_log_scale = d3.scale.log() - .range([h-log_m, m]) - .domain([d3.min(data, function(d) { return d.ratio; }), d3.max(data, function(d) { return d.ratio; })]); - - var color_scale = d3.scale.linear() - .domain([d3.min(data, function(d) { return d.ratio; }), 1, 10, d3.max(data, function(d) { return d.ratio; })]) - .range(["#9ABF00", "#9ABF00", "#CA46FF", "#CA46FF"]); - - // line - svg.selectAll('line.linear_regression') - .data([1]) - .enter() - .append('line') - .attr({ - 'class': 'linear_regression', - 'x1': m, - 'x2': w-m, - 'y1': function(d) { return y_log_scale(d); }, - 'y2': function(d) { return y_log_scale(d); }, - "stroke-width": 1.3, - "stroke": "white", - "stroke-dasharray": "7,5" - }); - - // circles w/ magic numbers :( - var circle_attr = { - 'cx': function(d) { return x_log_scale(d.extent) }, - 'cy': function(d) { return y_log_scale(d.ratio) }, - 'r': 5, - 'name': function(d) { return d.name; }, - 'class': function(d) { return d.enabled ? 'ball ball_link' : 'ball ball_nolink'; } - }; - - var data_ = [], - data_link_ = [], - exclude = []; - - _.each(data, function(row) { - if (!_.contains(exclude, row.name)) { - if (row.enabled === true) { - data_link_.push(row); - } else { - data_.push(row); - } - } - }); - - var circles_link = svg.selectAll('circle.ball_link') - .data(data_link_) - .enter() - .append('a') - .attr('xlink:href', function(d) { return '/country/' + d.iso}) - .append('svg:circle') - .attr(circle_attr) - .style('fill', function(d) { - return color_scale(d.ratio); - }) - .style('filter', 'url(#shadow)') - .on('mouseover', function() { - d3.select(d3.event.target) - .transition() - .attr('r', 7) - .style('opacity', 1); - - var t = $(this).offset().top - 80, - l = $(this).offset().left, - r = $(this).attr('r'), - tip = $('.tooltip').width()/2; - - that.tooltip.html($(this).attr('name')) - .style('visibility', 'visible') - .style('top', parseInt(t, 10)+'px') - .style('left', parseInt(l, 10)+parseInt(r, 10)-parseInt(tip, 10)-10+'px') - .attr('class', 'tooltip gain_tooltip'); - }) - .on('mouseenter', function() { - d3.select(d3.event.target) - .transition() - .attr('r', 7) - .style('opacity', 1); - - var t = $(this).offset().top - 80, - l = $(this).offset().left, - r = $(this).attr('r'), - tip = $('.tooltip').width()/2; - - that.tooltip.html($(this).attr('name')) - .style('visibility', 'visible') - .style('top', parseInt(t, 10)+'px') - .style('left', parseInt(l, 10)+parseInt(r, 10)-parseInt(tip, 10)+'px') - .attr('class', 'tooltip gain_tooltip'); - }) - .on('mouseout', function() { - d3.select(d3.event.target) - .transition() - .attr('r', 5) - .style('opacity', .8); - - that.tooltip.style('visibility', 'hidden'); - }); - - var circles = svg.selectAll('circle.ball_nolink') - .data(data_) - .enter() - .append('svg:circle') - .attr(circle_attr) - .style('fill', function(d) { - return color_scale(d.ratio); - }) - .style('filter', 'url(#shadow)') - .on('mouseover', function() { - d3.select(d3.event.target) - .transition() - .attr('r', 7) - .style('opacity', 1); - - var t = $(this).offset().top - 80, - l = $(this).offset().left, - r = $(this).attr('r'), - tip = $('.tooltip').width()/2; - - that.tooltip.html($(this).attr('name')) - .style('visibility', 'visible') - .style('top', parseInt(t, 10)+'px') - .style('left', parseInt(l, 10)+parseInt(r, 10)-parseInt(tip, 10)-10+'px') - .attr('class', 'tooltip gain_tooltip'); - }) - .on('mouseenter', function() { - d3.select(d3.event.target) - .transition() - .attr('r', 7) - .style('opacity', 1); - - var t = $(this).offset().top - 80, - l = $(this).offset().left, - r = $(this).attr('r'), - tip = $('.tooltip').width()/2; - - that.tooltip.html($(this).attr('name')) - .style('visibility', 'visible') - .style('top', parseInt(t, 10)+'px') - .style('left', parseInt(l, 10)+parseInt(r, 10)-parseInt(tip, 10)+'px') - .attr('class', 'tooltip gain_tooltip'); - }) - .on('mouseout', function() { - d3.select(d3.event.target) - .transition() - .attr('r', 5) - .style('opacity', .8); - - that.tooltip.style('visibility', 'hidden'); - }); - }); - } else if (this.model.get('graph') === 'domains') { - this._showYears(); - - var sql = 'SELECT name, '; - - for(var y = 2001; y < 2012; y++) { - sql += 'y'+y+', ' - } - - sql += 'y2012, GREATEST(' - - for(var y = 2001; y < 2012; y++) { - sql += 'y'+y+', ' - } - - sql += 'y2012) as max\ - FROM countries_domains'; - - d3.json('https://wri-01.cartodb.com/api/v2/sql?q='+sql, function(error, json) { - var data = json.rows; - - var r_scale = d3.scale.linear() - .range([5, 30]) // max ball radius - .domain([0, d3.max(data, function(d) { return d.max; })]) - - for(var j = 0; j < data.length; j++) { - var data_ = [], - domain = ''; - - _.each(data[j], function(val, key) { - if (key !== 'max') { - if (key === 'name') { - domain = val.toLowerCase(); - } else { - data_.push({ - 'year': key.replace('y',''), - 'value': val - }); - } - } - }); - - svg.append('text') - .attr('class', 'label') - .attr('id', 'label_'+domain) - .text(domain) - .attr('x', function() { - var l = x_scale(2002) - $(this).width()/2; - - return l; - }) - .attr('y', (h/5)*(j+.6)); - - var circle_attr = { - 'cx': function(d, i) { return x_scale(2001 + i); }, - 'cy': function(d) { return (h/5)*(j+1); }, - 'r': function(d) { return r_scale(d.value); }, - 'class': function(d) { return 'ball'; } - }; - - svg.selectAll('circle.domain_'+domain) - .data(data_) - .enter() - .append('svg:circle') - .attr(circle_attr) - .attr('data-slug', domain) - .attr('name', function(d) { - return ''+d.year+''+formatNumber(parseFloat(d.value/1000000).toFixed(1))+' Mha'; - }) - .style('fill', function(d) { return config.GRAPHCOLORS[domain]; }) - .on('mouseover', function() { - d3.select(d3.event.target) - .transition() - .attr('r', function(d) { return circle_attr.r(d) + 2; }) - .style('opacity', 1); - - var t = $(this).offset().top - 100, - l = $(this).offset().left, - r = $(this).attr('r'), - tip = $('.tooltip').width()/2, - slug = $(this).attr('data-slug'); - - that.tooltip.html($(this).attr('name')) - .style('visibility', 'visible') - .style('top', parseInt(t, 10)+'px') - .style('left', parseInt(l, 10)+parseInt(r, 10)-parseInt(tip, 10)-10+'px') - .attr('class', 'tooltip') - .attr('data-slug', 'tooltip') - .style('color', function() { - if (slug === 'subtropical') { - return '#FFC926' - } else { - return config.GRAPHCOLORS[slug]; - } - }); - }) - .on('mouseenter', function() { - d3.select(d3.event.target) - .transition() - .attr('r', function(d) { return circle_attr.r(d) + 2; }) - .style('opacity', 1); - - var t = $(this).offset().top - 80, - l = $(this).offset().left, - r = $(this).attr('r'), - tip = $('.tooltip').width()/2, - slug = $(this).attr('data-slug'); - - that.tooltip.html($(this).attr('name')) - .style('visibility', 'visible') - .style('top', parseInt(t, 10)+'px') - .style('left', parseInt(l, 10)+parseInt(r, 10)-parseInt(tip, 10)-10+'px') - .attr('class', 'tooltip') - .attr('data-slug', 'tooltip') - .style('color', function() { - if (domain === 'subtropical') { return config.GRAPHCOLORS[domain]; } - }); - }) - .on('mouseout', function() { - d3.select(d3.event.target) - .transition() - .attr('r', function(d) { return circle_attr.r(d); }) - .style('opacity', .8); - - that.tooltip - .style('color', '') - .style('visibility', 'hidden'); - }); - } - }); - } - } -}); - - -gfw.ui.view.CountriesEmbedShow = cdb.core.View.extend({ - el: document.body, - - events: { - 'click .forma_dropdown-link': '_openDropdown', - 'click .hansen_dropdown-link': '_openDropdown', - 'click .hansen_dropdown-menu a': '_redrawCircle' - }, - - initialize: function() { - this.iso = this.options.iso; - - this._initViews(); - this._initHansenDropdown(); - }, - - _initViews: function() { - this._drawCircle('forma', 'lines', { iso: this.iso }); - this._drawCircle('forest_loss', 'bars', { iso: this.iso, dataset: 'loss' }); - }, - - _initFormaDropdown: function() { - $('.forma_dropdown-link').qtip({ - show: 'click', - hide: { - event: 'click unfocus' - }, - content: { - text: $('.forma_dropdown-menu') - }, - position: { - my: 'bottom right', - at: 'top right', - target: $('.forma_dropdown-link'), - adjust: { - x: -10 - } - }, - style: { - tip: { - corner: 'bottom right', - mimic: 'bottom center', - border: 1, - width: 10, - height: 6 - } - } - }); - }, - - _initHansenDropdown: function() { - this.dropdown = $('.hansen_dropdown-link').qtip({ - show: 'click', - hide: { - event: 'click unfocus' - }, - content: { - text: $('.hansen_dropdown-menu') - }, - position: { - my: 'top right', - at: 'bottom right', - target: $('.hansen_dropdown-link'), - adjust: { - x: 10 - } - }, - style: { - tip: { - corner: 'top right', - mimic: 'top center', - border: 1, - width: 10, - height: 6 - } - } - }); - }, - - _openDropdown: function(e) { - e.preventDefault(); - }, - - _redrawCircle: function(e) { - e.preventDefault(); - - var dataset = $(e.target).attr('data-slug'), - subtitle = $(e.target).text(); - - var api = this.dropdown.qtip('api'); - - api.hide(); - - $('.hansen_dropdown-link').html(subtitle); - - if (dataset === 'countries_gain') { - this._drawCircle('forest_loss', 'comp', { iso: this.iso }); - } else { - this._drawCircle('forest_loss', 'bars', { iso: this.iso, dataset: dataset }); - } - }, - - _drawCircle: function(id, type, options) { - var that = this; - - var $graph = $('.'+id), - $amount = $('.'+id+' .graph-amount'), - $date = $('.'+id+' .graph-date'), - $coming_soon = $('.'+id+' .coming_soon'), - $action = $('.'+id+' .action'); - - $('.graph.'+id+' .frame_bkg').empty(); - $graph.addClass('ghost'); - $amount.html(''); - $date.html(''); - $coming_soon.hide(); - - var width = options.width || 256, - height = options.height || width, - h = 100, // maxHeight - radius = width / 2; - - var graph = d3.select('.graph.'+id+' .frame_bkg') - .append('svg:svg') - .attr('class', type) - .attr('width', width) - .attr('height', height); - - var dashedLines = [ - { x1:17, y:height/4, x2:239, color: '#ccc' }, - { x1:0, y:height/2, x2:width, color: '#ccc' }, - { x1:17, y:3*height/4, x2:239, color: '#ccc' } - ]; - - // Adds the dotted lines - _.each(dashedLines, function(line) { - graph.append('svg:line') - .attr('x1', line.x1) - .attr('y1', line.y) - .attr('x2', line.x2) - .attr('y2', line.y) - .style('stroke-dasharray', '2,2') - .style('stroke', line.color); - }); - - var sql = ["SELECT date_trunc('month', date) as date, COUNT(*) as alerts", - 'FROM forma_api', - "WHERE iso = '"+options.iso+"'", - "GROUP BY date_trunc('month', date)", - "ORDER BY date_trunc('month', date) ASC"].join(' '); - - if (type === 'lines') { - d3.json('https://wri-01.cartodb.com/api/v2/sql?q='+sql, function(json) { - if (json && json.rows.length > 0) { - $graph.removeClass('ghost'); - $action.removeClass('disabled'); - that._initFormaDropdown(); - - var data = json.rows.slice(1, json.rows.length); - } else { - $coming_soon.show(); - - return; - } - - var x_scale = d3.scale.linear() - .domain([0, data.length - 1]) - .range([0, width - 80]); - - var max = d3.max(data, function(d) { return parseFloat(d.alerts); }); - - if (max === d3.min(data, function(d) { return parseFloat(d.alerts); })) { - h = h/2; - } - - var y_scale = d3.scale.linear() - .domain([0, max]) - .range([0, h]); - - var line = d3.svg.line() - .x(function(d, i) { return x_scale(i); }) - .y(function(d, i) { return h - y_scale(d.alerts); }) - .interpolate('basis'); - - var marginLeft = 40, - marginTop = radius - h/2; - - $amount.html(''+formatNumber(data[data.length - 1].alerts)+''); - - var date = new Date(data[data.length - 1].date), - form_date = 'Alerts in ' + config.MONTHNAMES[date.getUTCMonth()] + ' ' + date.getUTCFullYear(); - - $date.html(form_date); - - var cx = width - 80 + marginLeft; - var cy = h - y_scale(data[data.length - 1].alerts) + marginTop; - - graph.append('svg:path') - .attr('transform', 'translate(' + marginLeft + ',' + marginTop + ')') - .attr('d', line(data)) - .on('mousemove', function(d) { - var index = Math.round(x_scale.invert(d3.mouse(this)[0])); - - if (data[index]) { // if there's data - $amount.html(''+formatNumber(data[index].alerts)+''); - - var date = new Date(data[index].date), - form_date = 'Alerts in ' + config.MONTHNAMES[date.getUTCMonth()] + ' ' + date.getUTCFullYear(); - - $date.html(form_date); - - var cx = d3.mouse(this)[0] + marginLeft; - var cy = h - y_scale(data[index].alerts) + marginTop; - - graph.select('.forma_marker') - .attr('cx', cx) - .attr('cy', cy); - } - }); - - graph.append('svg:circle') - .attr('class', 'forma_marker') - .attr('cx', cx) - .attr('cy', cy) - .attr('r', 5); - }); - } else if (type === 'bars') { - var sql = "SELECT "; - - if (options.dataset === 'loss') { - sql += "year, loss_gt_0 loss FROM umd WHERE iso='"+options.iso+"'"; - } else if (options.dataset === 'extent') { - sql += "year, extent_gt_25 extent FROM umd WHERE iso='"+options.iso+"'"; - } - - d3.json('https://wri-01.cartodb.com/api/v2/sql?q='+sql, function(json) { - if (json) { - $graph.removeClass('ghost'); - - var data = json.rows[0]; - } else { - $coming_soon.show(); - - return; - } - - var data_ = []; - - _.each(data, function(val, key) { - if (val.year >= 2001) { - data_.push({ - 'year': val.year, - 'value': eval('val.'+options.dataset) - }); - } - }); - - $amount.html(''+formatNumber(parseInt(data_[data_.length - 1].value, 10))+''); - $date.html('Hectares in ' + data_[data_.length - 1].year); - - var marginLeft = 40, - marginTop = radius - h/2 + 5; - - var y_scale = d3.scale.linear() - .domain([0, d3.max(data_, function(d) { return parseFloat(d.value); })]) - .range([height, marginTop*2]); - - var barWidth = (width - 80) / data_.length; - - var bar = graph.selectAll('g') - .data(data_) - .enter() - .append('g') - .attr('transform', function(d, i) { return 'translate(' + (marginLeft + i * barWidth) + ','+ -marginTop+')'; }); - - bar.append('svg:rect') - .attr('class', function(d, i) { - if (i === 11) { // last year index - return 'last bar' - } else { - return 'bar' - } - }) - .attr('y', function(d) { return y_scale(d.value); }) - .attr('height', function(d) { return height - y_scale(d.value); }) - .attr('width', barWidth - 1) - .on('mouseover', function(d) { - d3.selectAll('.bar').style('opacity', '.5'); - d3.select(this).style('opacity', '1'); - - $amount.html(''+formatNumber(parseInt(d.value, 10))+''); - $date.html('Hectares in ' + d.year); - }); - }); - } else if (type === 'comp') { - var sql = "SELECT iso, sum(umd.loss_gt_0) loss, max(umd.gain) gain FROM umd WHERE iso='"+options.iso+"'"; - - d3.json('https://wri-01.cartodb.com/api/v2/sql?q='+encodeURIComponent(sql), function(json) { - if (json) { - $graph.removeClass('ghost'); - - var data = json.rows[0]; - } else { - $coming_soon.show(); - - return; - } - - var data_ = [{ - 'key': 'Tree cover gain', - 'value': data.gain - }, { - 'key': 'Tree cover loss', - 'value': data.loss - }]; - - $amount.html(''+formatNumber(parseInt(data_[data_.length - 1].value, 10))+''); - $date.html('Ha '+data_[data_.length - 1].key); - - var barWidth = (width - 80) / 12; - - var marginLeft = 40 + barWidth*5, - marginTop = radius - h/2 + 5; - - var y_scale = d3.scale.linear() - .domain([0, d3.max(data_, function(d) { return parseFloat(d.value); })]) - .range([height, marginTop*2]); - - var bar = graph.selectAll('g') - .data(data_) - .enter() - .append('g') - .attr('transform', function(d, i) { return 'translate(' + (marginLeft + i * barWidth) + ',' + -marginTop + ')'; }); - - bar.append('svg:rect') - .attr('class', function(d, i) { - if (i === 1) { // last bar index - return 'last bar' - } else { - return 'bar' - } - }) - .attr('y', function(d) { return y_scale(d.value); }) - .attr('height', function(d) { return height - y_scale(d.value); }) - .attr('width', barWidth - 1) - .style('fill', '#FFC926') - .style('shape-rendering', 'crispEdges') - .on('mouseover', function(d) { - d3.selectAll('.bar').style('opacity', '.5'); - d3.select(this).style('opacity', '1'); - - $amount.html(''+formatNumber(parseFloat(d.value).toFixed(1))+''); - $date.html('Ha '+d.key); - }); - }); - } - } -}); +//= require_tree ./countries \ No newline at end of file diff --git a/app/assets/javascripts/countries/models/countries_overview.js b/app/assets/javascripts/countries/models/countries_overview.js new file mode 100644 index 0000000000..be1dd54b08 --- /dev/null +++ b/app/assets/javascripts/countries/models/countries_overview.js @@ -0,0 +1,7 @@ +gfw.ui.model.CountriesOverview = cdb.core.Model.extend({ + defaults: { + graph: 'total_loss', + years: true, + class: null + } +}); diff --git a/app/assets/javascripts/countries/models/country.js b/app/assets/javascripts/countries/models/country.js new file mode 100644 index 0000000000..e534927a2a --- /dev/null +++ b/app/assets/javascripts/countries/models/country.js @@ -0,0 +1,35 @@ +gfw.ui.model.Country = cdb.core.Model.extend({ + + initialize: function() { + }, + + url: function() { + return "http://wri-01.cartodb.com/api/v2/sql?q=SELECT cartodb_id, iso, id_1, name_1, bounds FROM gadm_1_all where iso = '" + this.get('iso') + "' order by id_1 asc"; + }, + + parse: function(response, options) { + var collection = new gfw.ui.collection.CountryAreas(); + + _.each(response.rows, function(row) { + var bounds = JSON.parse(row.bounds), + geojson = L.geoJson(bounds); + + collection.add(new gfw.ui.model.CountryArea({ + cartodb_id: row.cartodb_id, + iso: row.iso, + id_1: row.id_1, + name_1: row.name_1, + bounds: geojson.getBounds() + })) + }); + + return {fields: response.fields, areas: collection} + } + +}); + +gfw.ui.collection.CountryAreas = Backbone.Collection.extend({ + model: gfw.ui.model.CountryArea +}); + +gfw.ui.model.CountryArea = cdb.core.Model.extend({}); \ No newline at end of file diff --git a/app/assets/javascripts/countries/views/embed.js b/app/assets/javascripts/countries/views/embed.js new file mode 100644 index 0000000000..32749e4d7a --- /dev/null +++ b/app/assets/javascripts/countries/views/embed.js @@ -0,0 +1,358 @@ +gfw.ui.view.CountriesEmbedShow = cdb.core.View.extend({ + el: document.body, + + events: { + 'click .forma_dropdown-link': '_openDropdown', + 'click .hansen_dropdown-link': '_openDropdown', + 'click .hansen_dropdown-menu a': '_redrawCircle' + }, + + initialize: function() { + this.iso = this.options.iso; + + this._initViews(); + this._initHansenDropdown(); + }, + + _initViews: function() { + this._drawCircle('forma', 'lines', { iso: this.iso }); + this._drawCircle('forest_loss', 'bars', { iso: this.iso, dataset: 'loss' }); + }, + + _initFormaDropdown: function() { + $('.forma_dropdown-link').qtip({ + show: 'click', + hide: { + event: 'click unfocus' + }, + content: { + text: $('.forma_dropdown-menu') + }, + position: { + my: 'bottom right', + at: 'top right', + target: $('.forma_dropdown-link'), + adjust: { + x: -10 + } + }, + style: { + tip: { + corner: 'bottom right', + mimic: 'bottom center', + border: 1, + width: 10, + height: 6 + } + } + }); + }, + + _initHansenDropdown: function() { + this.dropdown = $('.hansen_dropdown-link').qtip({ + show: 'click', + hide: { + event: 'click unfocus' + }, + content: { + text: $('.hansen_dropdown-menu') + }, + position: { + my: 'top right', + at: 'bottom right', + target: $('.hansen_dropdown-link'), + adjust: { + x: 10 + } + }, + style: { + tip: { + corner: 'top right', + mimic: 'top center', + border: 1, + width: 10, + height: 6 + } + } + }); + }, + + _openDropdown: function(e) { + e.preventDefault(); + }, + + _redrawCircle: function(e) { + e.preventDefault(); + + var dataset = $(e.target).attr('data-slug'), + subtitle = $(e.target).text(); + + var api = this.dropdown.qtip('api'); + + api.hide(); + + $('.hansen_dropdown-link').html(subtitle); + + if (dataset === 'countries_gain') { + this._drawCircle('forest_loss', 'comp', { iso: this.iso }); + } else { + this._drawCircle('forest_loss', 'bars', { iso: this.iso, dataset: dataset }); + } + }, + + _drawCircle: function(id, type, options) { + var that = this; + + var $graph = $('.'+id), + $amount = $('.'+id+' .graph-amount'), + $date = $('.'+id+' .graph-date'), + $coming_soon = $('.'+id+' .coming_soon'), + $action = $('.'+id+' .action'); + + $('.graph.'+id+' .frame_bkg').empty(); + $graph.addClass('ghost'); + $amount.html(''); + $date.html(''); + $coming_soon.hide(); + + var width = options.width || 256, + height = options.height || width, + h = 100, // maxHeight + radius = width / 2; + + var graph = d3.select('.graph.'+id+' .frame_bkg') + .append('svg:svg') + .attr('class', type) + .attr('width', width) + .attr('height', height); + + var dashedLines = [ + { x1:17, y:height/4, x2:239, color: '#ccc' }, + { x1:0, y:height/2, x2:width, color: '#ccc' }, + { x1:17, y:3*height/4, x2:239, color: '#ccc' } + ]; + + // Adds the dotted lines + _.each(dashedLines, function(line) { + graph.append('svg:line') + .attr('x1', line.x1) + .attr('y1', line.y) + .attr('x2', line.x2) + .attr('y2', line.y) + .style('stroke-dasharray', '2,2') + .style('stroke', line.color); + }); + + var sql = ["SELECT date_trunc('month', date) as date, COUNT(*) as alerts", + 'FROM forma_api', + "WHERE iso = '"+options.iso+"'", + "GROUP BY date_trunc('month', date)", + "ORDER BY date_trunc('month', date) ASC"].join(' '); + + if (type === 'lines') { + d3.json('https://wri-01.cartodb.com/api/v2/sql?q='+sql, function(json) { + if (json && json.rows.length > 0) { + $graph.removeClass('ghost'); + $action.removeClass('disabled'); + that._initFormaDropdown(); + + var data = json.rows.slice(1, json.rows.length); + } else { + $coming_soon.show(); + + return; + } + + var x_scale = d3.scale.linear() + .domain([0, data.length - 1]) + .range([0, width - 80]); + + var max = d3.max(data, function(d) { return parseFloat(d.alerts); }); + + if (max === d3.min(data, function(d) { return parseFloat(d.alerts); })) { + h = h/2; + } + + var y_scale = d3.scale.linear() + .domain([0, max]) + .range([0, h]); + + var line = d3.svg.line() + .x(function(d, i) { return x_scale(i); }) + .y(function(d, i) { return h - y_scale(d.alerts); }) + .interpolate('basis'); + + var marginLeft = 40, + marginTop = radius - h/2; + + $amount.html(''+formatNumber(data[data.length - 1].alerts)+''); + + var date = new Date(data[data.length - 1].date), + form_date = 'Alerts in ' + config.MONTHNAMES[date.getUTCMonth()] + ' ' + date.getUTCFullYear(); + + $date.html(form_date); + + var cx = width - 80 + marginLeft; + var cy = h - y_scale(data[data.length - 1].alerts) + marginTop; + + graph.append('svg:path') + .attr('transform', 'translate(' + marginLeft + ',' + marginTop + ')') + .attr('d', line(data)) + .on('mousemove', function(d) { + var index = Math.round(x_scale.invert(d3.mouse(this)[0])); + + if (data[index]) { // if there's data + $amount.html(''+formatNumber(data[index].alerts)+''); + + var date = new Date(data[index].date), + form_date = 'Alerts in ' + config.MONTHNAMES[date.getUTCMonth()] + ' ' + date.getUTCFullYear(); + + $date.html(form_date); + + var cx = d3.mouse(this)[0] + marginLeft; + var cy = h - y_scale(data[index].alerts) + marginTop; + + graph.select('.forma_marker') + .attr('cx', cx) + .attr('cy', cy); + } + }); + + graph.append('svg:circle') + .attr('class', 'forma_marker') + .attr('cx', cx) + .attr('cy', cy) + .attr('r', 5); + }); + } else if (type === 'bars') { + var sql = "SELECT "; + + if (options.dataset === 'loss') { + sql += "year, loss_gt_0 loss FROM umd WHERE iso='"+options.iso+"'"; + } else if (options.dataset === 'extent') { + sql += "year, extent_gt_25 extent FROM umd WHERE iso='"+options.iso+"'"; + } + + d3.json('https://wri-01.cartodb.com/api/v2/sql?q='+sql, function(json) { + if (json) { + $graph.removeClass('ghost'); + + var data = json.rows[0]; + } else { + $coming_soon.show(); + + return; + } + + var data_ = []; + + _.each(data, function(val, key) { + if (val.year >= 2001) { + data_.push({ + 'year': val.year, + 'value': eval('val.'+options.dataset) + }); + } + }); + + $amount.html(''+formatNumber(parseInt(data_[data_.length - 1].value, 10))+''); + $date.html('Hectares in ' + data_[data_.length - 1].year); + + var marginLeft = 40, + marginTop = radius - h/2 + 5; + + var y_scale = d3.scale.linear() + .domain([0, d3.max(data_, function(d) { return parseFloat(d.value); })]) + .range([height, marginTop*2]); + + var barWidth = (width - 80) / data_.length; + + var bar = graph.selectAll('g') + .data(data_) + .enter() + .append('g') + .attr('transform', function(d, i) { return 'translate(' + (marginLeft + i * barWidth) + ','+ -marginTop+')'; }); + + bar.append('svg:rect') + .attr('class', function(d, i) { + if (i === 11) { // last year index + return 'last bar' + } else { + return 'bar' + } + }) + .attr('y', function(d) { return y_scale(d.value); }) + .attr('height', function(d) { return height - y_scale(d.value); }) + .attr('width', barWidth - 1) + .on('mouseover', function(d) { + d3.selectAll('.bar').style('opacity', '.5'); + d3.select(this).style('opacity', '1'); + + $amount.html(''+formatNumber(parseInt(d.value, 10))+''); + $date.html('Hectares in ' + d.year); + }); + }); + } else if (type === 'comp') { + var sql = "SELECT iso, sum(umd.loss_gt_0) loss, max(umd.gain) gain FROM umd WHERE iso='"+options.iso+"'"; + + d3.json('https://wri-01.cartodb.com/api/v2/sql?q='+encodeURIComponent(sql), function(json) { + if (json) { + $graph.removeClass('ghost'); + + var data = json.rows[0]; + } else { + $coming_soon.show(); + + return; + } + + var data_ = [{ + 'key': 'Tree cover gain', + 'value': data.gain + }, { + 'key': 'Tree cover loss', + 'value': data.loss + }]; + + $amount.html(''+formatNumber(parseInt(data_[data_.length - 1].value, 10))+''); + $date.html('Ha '+data_[data_.length - 1].key); + + var barWidth = (width - 80) / 12; + + var marginLeft = 40 + barWidth*5, + marginTop = radius - h/2 + 5; + + var y_scale = d3.scale.linear() + .domain([0, d3.max(data_, function(d) { return parseFloat(d.value); })]) + .range([height, marginTop*2]); + + var bar = graph.selectAll('g') + .data(data_) + .enter() + .append('g') + .attr('transform', function(d, i) { return 'translate(' + (marginLeft + i * barWidth) + ',' + -marginTop + ')'; }); + + bar.append('svg:rect') + .attr('class', function(d, i) { + if (i === 1) { // last bar index + return 'last bar' + } else { + return 'bar' + } + }) + .attr('y', function(d) { return y_scale(d.value); }) + .attr('height', function(d) { return height - y_scale(d.value); }) + .attr('width', barWidth - 1) + .style('fill', '#FFC926') + .style('shape-rendering', 'crispEdges') + .on('mouseover', function(d) { + d3.selectAll('.bar').style('opacity', '.5'); + d3.select(this).style('opacity', '1'); + + $amount.html(''+formatNumber(parseFloat(d.value).toFixed(1))+''); + $date.html('Ha '+d.key); + }); + }); + } + } +}); \ No newline at end of file diff --git a/app/assets/javascripts/countries/views/header.js b/app/assets/javascripts/countries/views/header.js new file mode 100644 index 0000000000..ac5399770e --- /dev/null +++ b/app/assets/javascripts/countries/views/header.js @@ -0,0 +1,538 @@ +gfw.ui.model.CountryHeaderStatus = cdb.core.Model.extend({}); + +gfw.ui.model.layersOptions = Backbone.Model.extend({ + + initialize: function(options) { + options = options || {}; + + var layers = { + 'forest2000': { + url: 'http://earthengine.google.org/static/hansen_2013/tree_alpha/%z/%x/%y.png', + dataMaxZoom: 12, + tileSize: [256, 256], + _filterCanvasImage: function(imageData, w, h) { + var components = 4, + pixelPos; + + for(var i = 0 ; i < w; ++i) { + for(var j = 0; j < h; ++j) { + var pixelPos = (j * w + i) * components, + intensity = imageData[pixelPos + 3]; + + imageData[pixelPos] = 0; + imageData[pixelPos + 1] = intensity * 0.7; + imageData[pixelPos + 2] = 0; + imageData[pixelPos+ 3] = intensity * 0.7; + } + } + } + } + }; + + var self = this, + layer = layers[options.layer]; + + if (layer) { + _.each(layer, function(row, i) { + self.set(i, row); + }) + } + }, + + +}); + +gfw.ui.view.leafletCanvasLayer = Backbone.View.extend({ + + initialize: function(options) { + var self = this; + options = options || {layerName: '', mapOptions: {}}; + + this.layerOptions = new gfw.ui.model.layersOptions({layer: options.layerName}); + _.extend(this, this.layerOptions.toJSON()); + + this.layer = L.tileLayer.canvas(options.mapOptions); + + this.layer.drawTile = function(canvas, tilePoint, zoom) { + var xhr = new XMLHttpRequest(), + ctx = canvas.getContext('2d'); + + var x = tilePoint.x, + y = tilePoint.y, + z = zoom, + mz = self.dataMaxZoom; + + if (zoom > mz) { + x = Math.floor(x / (Math.pow(2, zoom - mz))); + y = Math.floor(y / (Math.pow(2, zoom - mz))); + z = mz; + } else { + y = (y > Math.pow(2, z) ? y % Math.pow(2,z) : y); + if (x >= Math.pow(2, z)) { + x = x % Math.pow(2, z); + } else if (x < 0) { + x = Math.pow(2,z) - Math.abs(x); + } + } + + var url = self.url.replace('%z', z).replace('%x', x).replace('%y', y); + + xhr.onload = function () { + var url = URL.createObjectURL(this.response), + image = new Image(); + + image.onload = function () { + image.crossOrigin = ''; + + canvas.image = image; + canvas.coord = {x: tilePoint.x, y: tilePoint.y}; + canvas.coord.z = zoom; + + self._drawImageCanvas(canvas); + + URL.revokeObjectURL(url); + }; + + image.src = url; + }; + + xhr.open('GET', url, true); + xhr.responseType = 'blob'; + xhr.send(); + } + }, + + _filterCanvasImage: function(imageData, w, h) { + var components = 4, + pixelPos; + + for(var i = 0 ; i < w; ++i) { + for(var j = 0; j < h; ++j) { + var pixelPos = (j * w + i) * components, + intensity = imageData[pixelPos + 3]; + + imageData[pixelPos] = 0; + imageData[pixelPos + 1] = intensity * 0.7; + imageData[pixelPos + 2] = 255; + imageData[pixelPos+ 3] = intensity * 0.7; + } + } + }, + + _drawImageCanvas: function(canvas) { + var ctx = canvas.getContext('2d'), + coord = canvas.coord; + + if (canvas.coord) { + var zsteps = coord.z - this.dataMaxZoom; + + if (zsteps > 0) { + ctx['imageSmoothingEnabled'] = false; + ctx['mozImageSmoothingEnabled'] = false; + ctx['webkitImageSmoothingEnabled'] = false; + + var srcX = 256 / Math.pow(2, zsteps) * (coord.x % Math.pow(2, zsteps)), + srcY = 256 / Math.pow(2, zsteps) * (coord.y % Math.pow(2, zsteps)), + srcW = 256 / Math.pow(2, zsteps), + srcH = 256 / Math.pow(2, zsteps); + ctx.clearRect(0, 0, 256, 256); + ctx.drawImage(canvas.image, srcX, srcY, srcW, srcH, 0, 0, 256, 256); + + } else { + try { + ctx.drawImage(canvas.image, 0, 0); + } catch(err) { } + } + + var I = ctx.getImageData(0, 0, canvas.width, canvas.height); + this._filterCanvasImage(I.data, canvas.width, canvas.height); + ctx.putImageData(I, 0, 0); + } + }, + + getLayer: function() { + return this.layer; + } + +}); + +gfw.ui.view.CountryHeader = cdb.core.View.extend({ + + el: $('.country-header'), + + events: { + 'change #areaSelector': '_onSelectArea', + 'click .selector-remove': '_navigateCountry' + }, + + initialize: function(options) { + _.extend(this, options); + var self = this; + + _.bindAll(this, '_cartodbLayerDone'); + + // Cache + this.$areaSelector = this.$('#areaSelector'); + this.$selectorRemove = this.$('.selector-remove'); + this.$map = this.$('.map'); + + var Router = Backbone.Router.extend({ + routes: { + 'country/:id': 'loadCountry', + 'country/:id/': 'loadCountry', + 'country/:id/:areaId': 'loadArea', + 'country/:id/:areaId/': 'loadArea' + }, + + initialize: function() { + self._drawLossAndGain(); + }, + + loadArea: function(countryId, areaId) { + var area = self.country.get('areas').where({ id_1: Number(areaId) })[0]; + self.area = area; + if (!self.map) { + self._setAreaSelector(); + self._initMap(function() { + self._displayArea(area); + }); + } else { + self._displayArea(area); + } + }, + + loadCountry: function(countryId) { + if (!self.map) { + self._setAreaSelector(); + self._initMap(function() { + self._displayCountry(); + }); + } else { + self._displayCountry(); + } + } + + }); + + this.country.fetch({ + success: function() { + self.router = new Router(); + Backbone.history.start({pushState: true}); + } + }); + }, + + _setAreaSelector: function() { + var self = this; + + _.each(this.country.get('areas').models, function(area) { + if (self.area == area) { + self.$areaSelector.append('') + } else { + self.$areaSelector.append('') + } + }); + }, + + _onSelectArea: function() { + var self = this, + areaName = this.$areaSelector.val(), + area = this.country.get('areas').where({ name_1: areaName })[0]; + + if (area) { + this.router.navigate('country/' + this.country.get('iso') + '/' + String(area.get('id_1')), {trigger: true}); + } else { + this._navigateCountry(); + } + }, + + _navigateCountry: function() { + this.router.navigate('country/' + this.country.get('iso'), {trigger: true}); + }, + + _initMap: function(callback) { + var self = this, + sql = new cartodb.SQL({ user: 'wri-01' }); + + sql.execute("SELECT bounds FROM country_mask WHERE code = '" + this.country.get('iso') + "'") + .done(function(data) { + var geojson = L.geoJson(JSON.parse(data.rows[0].bounds)), + bounds = geojson.getBounds(); + + self.country.set('bounds', bounds); + self._renderMap(callback); + }); + }, + + _renderMap: function(callback) { + var self = this; + + this.map = new L.Map('countryMap', { + center: [0, 0], + zoom: 3, + zoomControl: false, + dragging: false, + touchZoom: false, + doubleClickZoom: false, + scrollWheelZoom: false, + attributionControl: false, + zoomAnimation: false, + fadeAnimation: false, + }); + + this.forestLayer = new gfw.ui.view.leafletCanvasLayer({ + layerName: 'forest2000', + mapOptions: { + opacity: 0 + } + }).getLayer().addTo(this.map); + + callback(); + }, + + _removeCartodblayer: function() { + if (this.cartodbLayer) { + this.cartodbLayer.remove(); + } + }, + + _displayCountry: function() { + var self = this; + + this.map.fitBounds(this.country.get('bounds')); + this._removeCartodblayer(); + + this.$selectorRemove.hide(); + this.$areaSelector.val(''); + + cartodb.createLayer(this.map, { + user_name: 'wri-01', + type: 'cartodb', + cartodb_logo: false, + sublayers: [{ + sql: "SELECT * FROM country_mask", + cartocss: "\ + #country_mask {\ + polygon-fill: #373442;\ + polygon-opacity: 1;\ + line-color: #373442;\ + line-width: 1;\ + line-opacity: 1;\ + }\ + #country_mask[code='" + this.country.get('iso') + "'] {\ + polygon-opacity: 0;\ + line-color: #73707D;\ + line-width: 1;\ + line-opacity: 1;\ + }" + }] + }) + .addTo(this.map) + .done(this._cartodbLayerDone); + }, + + _displayArea: function(area) { + var self = this; + + this.map.fitBounds(area.get('bounds'), {reset: true}); + this._removeCartodblayer(); + + this.$selectorRemove.show(); + + cartodb.createLayer(this.map, { + user_name: 'wri-01', + type: 'cartodb', + cartodb_logo: false, + sublayers: [{ + sql: "SELECT * FROM country_mask", + cartocss: "\ + #country_mask {\ + polygon-fill: #373442;\ + polygon-opacity: 1;\ + line-color: #373442;\ + line-width: 1;\ + line-opacity: 1;\ + }\ + #country_mask[code='" + this.country.get('iso') + "'] {\ + polygon-opacity: 0;\ + line-color: #373442;\ + line-width: 1;\ + line-opacity: 1;\ + }" + }, { + sql: "SELECT * FROM gadm_1_all", + cartocss: "\ + #gadm_1_all {\ + polygon-fill: #373442;\ + polygon-opacity: 1;\ + line-color: #373442;\ + line-width: 1;\ + line-opacity: 1;\ + [cartodb_id=" + area.get('cartodb_id') + "]{\ + polygon-opacity: 0;\ + }\ + ::active[cartodb_id=" + area.get('cartodb_id') + "] {\ + polygon-opacity: 0;\ + line-color: #73707D;\ + line-width: 1;\ + line-opacity: 1;\ + }\ + }" + }] + }) + .addTo(this.map) + .done(this._cartodbLayerDone); + }, + + _cartodbLayerDone: function(layer) { + var self = this; + + this.cartodbLayer = layer; + + this.cartodbLayer.on('loading' , function() { + self.$map.removeClass('loaded'); + self.forestLayer.setOpacity(0); + }); + + this.cartodbLayer.on('load' , function() { + self.$map.addClass('loaded'); + self.forestLayer.setOpacity(1); + }); + }, + + _drawLossAndGain: function() { + var sql = "SELECT year, loss_gt_0 loss FROM umd WHERE iso='" + this.country.get('iso') + "' ORDER BY year ASC", + that = this; + + var $graph = this.$('.loss-gain-graph'), + $comingSoon = $graph.find('.coming-soon'), + $amount = $graph.find('.graph-amount'), + $date = $graph.find('.graph-date'); + + var width = 200, + height = 90, + h = 90, // maxHeight + radius = width / 2; + + var graph = d3.select('.loss-gain-graph .graph') + .append('svg:svg') + .attr('width', width) + .attr('height', height); + + d3.json('https://wri-01.cartodb.com/api/v2/sql?q=' + sql, function(json) { + if (json) { + $graph.removeClass('ghost'); + var data = json.rows; + } else { + $comingSoon.show(); + return; + } + + var data_ = []; + + _.each(data, function(val, key) { + if (val.year >= 2001) { + data_.push({ + 'year': val.year, + 'value': val.loss//eval('val.' + options.dataset) + }); + } + }); + + $amount.html('' + formatNumber(parseInt(data_[data_.length - 1].value, 10)) + ''); + $date.html('Hectares lost in ' + data_[data_.length - 1].year); + + var marginLeft = 5, + marginTop = 0; + + var y_scale = d3.scale.linear() + .domain([0, d3.max(data_, function(d) { return parseFloat(d.value); })]) + .range([height, marginTop * 2]); + + var barWidth = 16; + + var bar = graph.selectAll('g') + .data(data_) + .enter() + .append('g') + .attr('transform', function(d, i) { return 'translate(' + (marginLeft + i * barWidth) + ',' + -marginTop + ')'; }); + + bar.append('svg:rect') + .attr('class', 'bar') + .attr('y', function(d) { return y_scale(d.value); }) + .attr('height', function(d) { return height - y_scale(d.value); }) + .attr('width', barWidth - 2) + .style('fill', function(d, i) { + if (i === 11) { + return '#9FBA2B'; + } else { + return '#524F5C'; + } + }) + .on('mouseover', function(d) { + d3.selectAll('.bar').style('fill', '#524F5C'); + d3.select(this).style('fill', '#9FBA2B'); + + $amount.html('' + formatNumber(parseInt(d.value, 10)) + ''); + $date.html('Hectares lost in ' + d.year); + }); + + // Draw gain line + var sql = 'SELECT y2001_y2012 as gain, (SELECT SUM('; + + for(var y = 2001; y < 2012; y++) { + sql += 'y'+y+' + '; + } + + sql += ['y2012) FROM countries_loss', + "WHERE iso = '" + that.country.get('iso') + "') as loss", + 'FROM countries_gain', + "WHERE iso = '" + that.country.get('iso') + "'"].join(' '); + + d3.json('https://wri-01.cartodb.com/api/v2/sql?q=' + encodeURIComponent(sql), function(json) { + var gainAverage = json.rows[0].gain / 12; + + var tooltip = d3.select('.loss-gain-graph') + .append('div') + .attr('class', 'gain-tooltip') + .style('visibility', 'hidden') + .text(formatNumber(parseInt(gainAverage), 10)); + + tooltip + .append('span') + .text('Average Ha of gain/year'); + + var gainLine = graph.append('svg:rect') + .attr('class', 'gain-line') + .attr('x', 0) + .attr('y', Math.abs(y_scale(gainAverage))) + .attr('height', 1) + .attr('width', width) + .style('stroke', 'transparent') + .style("stroke-width", "5") + .style('fill', '#ccc'); + + gainLine + .on('mousemove', function() { + d3.select('.gain-line') + .style('height', 2) + .style('fill', 'white'); + + tooltip + .style('visibility', 'visible') + .style("top", Math.abs(y_scale(gainAverage)) + "px") + .style("left", (d3.mouse(this)[0] - 58) + "px"); + }) + .on('mouseout', function() { + d3.select('.gain-line') + .style('height', 1) + .style('fill', '#ccc'); + + tooltip.style('visibility', 'hidden'); + }) + }); + + }); + } + +}); \ No newline at end of file diff --git a/app/assets/javascripts/countries/views/index.js b/app/assets/javascripts/countries/views/index.js new file mode 100644 index 0000000000..767ee49745 --- /dev/null +++ b/app/assets/javascripts/countries/views/index.js @@ -0,0 +1,37 @@ +gfw.ui.view.CountriesIndex = cdb.core.View.extend({ + el: document.body, + + initialize: function() { + this._drawCountries(); + }, + + _drawCountries: function() { + var that = this; + + var sql = ['SELECT c.iso, c.enabled, m.the_geom', + 'FROM ne_50m_admin_0_countries m, gfw2_countries c', + 'WHERE c.iso = m.adm0_a3 AND c.enabled', + '&format=topojson'].join(' '); + + var sql_ = ['SELECT c.iso, m.the_geom', + 'FROM ne_50m_admin_0_countries m, gfw2_countries c', + 'WHERE c.iso = m.adm0_a3', + "AND c.iso = 'TWN'&format=topojson"].join(' '); + + d3.json('https://wri-01.cartodb.com/api/v2/sql?q='+sql, function(error, topology) { + for (var i = 0; i < Object.keys(topology.objects).length; i++) { + var iso = topology.objects[i].properties.iso; + + var bounds = draw(topology, i, iso, { alerts: false }); + + if (iso === 'CHN') { + that.bounds = bounds; + + d3.json('https://wri-01.cartodb.com/api/v2/sql?q='+sql_, function(error, topology) { + draw(topology, 0, 'CHN', { alerts: false, bounds: that.bounds}); + }); + } + } + }); + } +}); \ No newline at end of file diff --git a/app/assets/javascripts/countries/views/overview.js b/app/assets/javascripts/countries/views/overview.js new file mode 100644 index 0000000000..31e00f1ff4 --- /dev/null +++ b/app/assets/javascripts/countries/views/overview.js @@ -0,0 +1,1631 @@ +gfw.ui.view.CountriesOverview = cdb.core.View.extend({ + el: document.body, + + events: { + 'click .info': '_openSource', + 'click .graph_tab': '_updateGraph', + 'click .countries_list__footer': '_drawList' + }, + + initialize: function() { + this.model = new gfw.ui.model.CountriesOverview(); + + this.$graph = $('.overview_graph__area'); + this.$years = $('.overview_graph__years'); + + var m = this.m = 40, + w = this.w = this.$graph.width()+(m*2), + h = this.h = this.$graph.height(), + vertical_m = this.vertical_m = 20; + + this.x_scale = d3.scale.linear() + .range([m, w-m]) + .domain([2001, 2012]); + + this.grid_scale = d3.scale.linear() + .range([vertical_m, h-vertical_m]) + .domain([0, 1]); + + this.model.bind('change:graph', this._redrawGraph, this); + this.model.bind('change:years', this._toggleYears, this); + this.model.bind('change:class', this._toggleClass, this); + + this._initViews(); + }, + + _initViews: function() { + this.sourceWindow = new gfw.ui.view.SourceWindow(); + this.$el.append(this.sourceWindow.render()); + + this.tooltip = d3.select('body') + .append('div') + .attr('class', 'tooltip'); + + this._drawYears(); + this._drawGraph(); + this._drawList(); + + Share = new gfw.ui.view.Share(); + this.$el.find('.overview_graph').append(Share.render()); + }, + + _openSource: function(e) { + e.preventDefault(); + + var source = $(e.target).closest('.info').attr('data-source'); + + ga('send', 'event', 'SourceWindow', 'Open', source); + this.sourceWindow.show(source).addScroll(); + }, + + _toggleYears: function() { + var that = this; + + if (this.model.get('years') === false) { + this.$years.slideUp(250, function() { + $('.overview_graph__axis').slideDown(); + }); + } else { + $('.overview_graph__axis').slideUp(250, function() { + that.$years.slideDown(); + }); + } + }, + + _showYears: function() { + if (!this.model.get('years')) { + this.model.set('years', true); + } + }, + + _hideYears: function() { + if (this.model.get('years')) { + this.model.set('years', false); + } + }, + + _updateGraph: function(e) { + e.preventDefault(); + + var $target = $(e.target).closest('.graph_tab'), + graph = $target.attr('data-slug'); + + if (graph === this.model.get('graph')) { + return; + } else { + $('.graph_tab').removeClass('selected'); + $target.addClass('selected'); + + this.model.set('graph', graph); + } + }, + + _redrawGraph: function() { + var graph = this.model.get('graph'); + + $('.overview_graph__title').html(config.GRAPHS[graph].title); + $('.overview_graph__legend p').html(config.GRAPHS[graph].subtitle); + $('.overview_graph__legend .info').attr('data-source', graph); + + this.$graph.find('.'+graph); + + this.$graph.find('.chart').hide(); + this.$graph.find('.'+graph).fadeIn(); + + this._drawGraph(); + this._drawList(); + }, + + _drawList: function(e) { + var that = this; + + e && e.preventDefault(); + + if (this.model.get('graph') === 'total_loss') { + var sql = 'WITH loss as (SELECT iso, SUM('; + + for(var y = 2001; y < 2012; y++) { + sql += 'y'+y+' + '; + } + + sql += 'y2012) as sum_loss\ + FROM countries_loss\ + GROUP BY iso)'; + + sql += 'SELECT c.iso, c.name, c.enabled, sum_loss\ + FROM loss, gfw2_countries c\ + WHERE loss.iso = c.iso\ + AND NOT sum_loss = 0\ + ORDER BY sum_loss DESC '; + + if (e) { + sql += 'OFFSET 10'; + } else { + sql += 'LIMIT 10'; + } + + d3.json('http://wri-01.cartodb.com/api/v2/sql/?q='+encodeURIComponent(sql), function(json) { + var self = that, + markup_list = ''; + + var data = json.rows; + + _.each(data, function(val, key) { + var ord = e ? (key+11) : (key+1), + enabled = val.enabled ? ''+val.name+'' : val.name; + + markup_list += '
  • \ +
    \ +
    '+ord+'
    \ +
    '+enabled+'
    \ +
  • '; + }); + + if (e) { + $('.countries_list__footer').fadeOut(); + } else { + $('.countries_list ul').html(''); + $('.countries_list__footer').show(); + + $('.countries_list__header__minioverview').html('Loss vs Gain'); + } + + $('.countries_list ul').append(markup_list); + + that.model.set('class', null); + + _.each(data, function(val, key) { + self._drawMiniOverview(val.iso); + }); + }); + } else if (this.model.get('graph') === 'percent_loss') { + var sql = 'SELECT c.iso, c.name, c.enabled, loss_y2001_y2012 as ratio_loss\ + FROM countries_percent percent, gfw2_countries c\ + WHERE percent.iso = c.iso AND c.enabled IS true\ + AND NOT loss_y2001_y2012 = 0\ + ORDER BY ratio_loss DESC '; + + if (e) { + sql += 'OFFSET 10'; + } else { + sql += 'LIMIT 10'; + } + + d3.json('http://wri-01.cartodb.com/api/v2/sql/?q='+encodeURIComponent(sql), function(json) { + var self = that, + markup_list = ''; + + var data = json.rows; + + _.each(data, function(val, key) { + var ord = e ? (key+11) : (key+1), + enabled = val.enabled ? ''+val.name+'' : val.name; + + markup_list += '
  • \ +
    \ +
    '+ord+'
    \ +
    '+enabled+'
    \ +
  • '; + }); + + if (e) { + $('.countries_list__footer').fadeOut(); + } else { + $('.countries_list ul').html(''); + $('.countries_list__footer').show(); + + $('.countries_list__header__minioverview').html('% Loss'); + } + + $('.countries_list ul').append(markup_list); + + that.model.set('class', null); + + _.each(data, function(val, key) { + self._drawMiniOverview(val.iso); + }); + }); + } else if (this.model.get('graph') === 'total_extent') { + var sql = 'WITH extent as (SELECT iso, SUM('; + + for(var y = 2001; y < 2012; y++) { + sql += 'y'+y+' + '; + } + + sql += 'y2012) as sum_extent\ + FROM extent_gt_25\ + GROUP BY iso)'; + + sql += 'SELECT c.iso, c.name, c.enabled, sum_extent\ + FROM extent, gfw2_countries c\ + WHERE extent.iso = c.iso\ + AND NOT sum_extent = 0\ + ORDER BY sum_extent DESC '; + + if (e) { + sql += 'OFFSET 10'; + } else { + sql += 'LIMIT 10'; + } + + d3.json('http://wri-01.cartodb.com/api/v2/sql/?q='+encodeURIComponent(sql), function(json) { + var self = that, + markup_list = ''; + + var data = json.rows; + + _.each(data, function(val, key) { + var ord = e ? (key+11) : (key+1), + enabled = val.enabled ? ''+val.name+'' : val.name; + + markup_list += '
  • \ +
    \ +
    '+ord+'
    \ +
    '+enabled+'
    \ +
  • '; + }); + + if (e) { + $('.countries_list__footer').fadeOut(); + } else { + $('.countries_list ul').html(''); + $('.countries_list__footer').show(); + + $('.countries_list__header__minioverview').html('Cover extent vs Cover loss'); + } + + $('.countries_list ul').append(markup_list); + + that.model.set('class', 'expanded'); + + _.each(data, function(val, key) { + self._drawMiniOverview(val.iso); + }); + }); + } else if (this.model.get('graph') === 'ratio') { + var sql = 'WITH loss as (SELECT iso, SUM('; + + for(var y = 2001; y < 2012; y++) { + sql += 'loss.y'+y+' + '; + } + + sql += 'loss.y2012) as sum_loss\ + FROM loss_gt_50 loss\ + GROUP BY iso), gain as (SELECT g.iso, SUM(y2001_y2012) as sum_gain\ + FROM countries_gain g, loss_gt_50 loss\ + WHERE loss.iso = g.iso\ + GROUP BY g.iso), ratio as ('; + + sql += 'SELECT c.iso, c.name, c.enabled, loss.sum_loss/gain.sum_gain as ratio\ + FROM loss, gain, gfw2_countries c\ + WHERE sum_gain IS NOT null\ + AND NOT sum_gain = 0\ + AND c.iso = gain.iso\ + AND c.iso = loss.iso\ + ORDER BY loss.sum_loss DESC\ + LIMIT 50) '; + + sql += 'SELECT *\ + FROM ratio\ + WHERE ratio IS NOT null\ + ORDER BY ratio DESC '; + + if (e) { + sql += ['OFFSET 10', + 'LIMIT 40'].join('\n'); + } else { + sql += 'LIMIT 10'; + } + + d3.json('http://wri-01.cartodb.com/api/v2/sql/?q='+encodeURIComponent(sql), function(json) { + var self = that, + markup_list = ''; + + var data = json.rows; + + _.each(data, function(val, key) { + var ord = e ? (key+11) : (key+1), + enabled = val.enabled ? ''+val.name+'' : val.name; + + markup_list += '
  • \ +
    '+formatNumber(parseFloat(val.ratio).toFixed(2))+'
    \ +
    '+ord+'
    \ +
    '+enabled+'
    \ +
  • '; + }); + + if (e) { + $('.countries_list__footer').fadeOut(); + } else { + $('.countries_list ul').html(''); + $('.countries_list__footer').show(); + + $('.countries_list__header__minioverview').html('Ratio of Loss to Gain'); + } + + $('.countries_list ul').append(markup_list); + + that.model.set('class', 'medium'); + + _.each(data, function(val, key) { + self._drawMiniOverview(val.iso); + }); + }); + } else if (this.model.get('graph') === 'domains') { + var sql = 'SELECT name, total_loss, total_gain, GREATEST(' + + for(var y = 2001; y < 2012; y++) { + sql += 'y'+y+', ' + } + + sql += 'y2012) as max\ + FROM countries_domains\ + ORDER BY total_loss DESC '; + + d3.json('http://wri-01.cartodb.com/api/v2/sql/?q='+encodeURIComponent(sql), function(json) { + var self = that, + markup_list = ''; + + var data = json.rows; + + _.each(data, function(val, key) { + markup_list += ['
  • ', + '
    ', + '
    '+formatNumber(parseFloat(val.total_loss/1000000).toFixed(1))+' Mha
    ', + '
    '+formatNumber(parseFloat(val.total_gain/1000000).toFixed(1))+' Mha
    ', + '
    ', + '
    '+(key+1)+'
    ', + '
    '+val.name+'
    ', + '
  • '].join(''); + }); + + $('.countries_list__footer').hide(); + $('.countries_list__header__minioverview').html('Total loss vs Total gain'); + $('.countries_list ul').html(markup_list); + + that.model.set('class', 'huge'); + + _.each(data, function(val, key) { + self._drawMiniOverview(val.iso); + }); + }); + } + }, + + _toggleClass: function() { + if (this.model.get('class') === 'expanded') { + $('.countries_list__header__minioverview').addClass('expanded'); + $('.countries_list__minioverview').addClass('expanded'); + + $('.countries_list__header__minioverview').removeClass('medium huge'); + $('.countries_list__minioverview').removeClass('medium huge'); + } else if (this.model.get('class') === 'medium') { + $('.countries_list__header__minioverview').addClass('medium'); + $('.countries_list__minioverview').addClass('medium'); + + $('.countries_list__header__minioverview').removeClass('expanded huge'); + $('.countries_list__minioverview').removeClass('expanded huge'); + } else if (this.model.get('class') === 'huge') { + $('.countries_list__header__minioverview').addClass('huge'); + $('.countries_list__minioverview').addClass('huge'); + + $('.countries_list__header__minioverview').removeClass('expanded medium'); + $('.countries_list__minioverview').removeClass('expanded medium'); + } else { + $('.countries_list__header__minioverview').removeClass('expanded medium huge'); + $('.countries_list__minioverview').removeClass('expanded medium huge'); + } + }, + + _drawMiniOverview: function(iso) { + var width = 90, + height = 30; + + var graph = d3.select('.countries_list__minioverview_'+iso) + .append('svg:svg') + .attr('width', width) + .attr('height', height); + + if (this.model.get('graph') === ('total_loss')) { + var sql = 'SELECT '; + + for(var y = 2001; y < 2012; y++) { + sql += 'y'+y+', ' + } + + sql += "y2012, (SELECT y2001_y2012\ + FROM countries_gain\ + WHERE c.iso = iso) as gain\ + FROM loss_gt_0 c \ + WHERE iso = '"+iso+"'"; + + d3.json('https://wri-01.cartodb.com/api/v2/sql?q='+sql, function(json) { + var data = json.rows[0]; + + var data_ = [], + gain = null; + + _.each(data, function(val, key) { + if (key === 'gain') { + gain = val/12; + } else { + data_.push({ + 'year': key.replace('y',''), + 'value': val + }); + } + }); + + var y_scale = d3.scale.linear() + .domain([0, d3.max(data_, function(d) { return d.value; })]) + .range([height, 0]); + + var barWidth = width / data_.length; + + var bar = graph.selectAll('g') + .data(data_) + .enter().append('g') + .attr('transform', function(d, i) { return 'translate(' + (i * barWidth) + ', 0)'; }); + + bar.append('svg:rect') + .attr('class', 'bar') + .attr('y', function(d) { return y_scale(d.value); }) + .attr('height', function(d) { return height - y_scale(d.value); }) + .attr('width', barWidth - 1); + + var data_gain_ = [ + { + year: 2001, + value: gain + }, + { + year: 2012, + value: gain + } + ]; + + graph.selectAll('line.minioverview_line') + .data(data_gain_) + .enter() + .append('line') + .attr({ + 'class': 'minioverview_line', + 'x1': 0, + 'x2': width, + 'y1': function(d) { return y_scale(gain); }, + 'y2': function(d) { return y_scale(gain); } + }); + }); + } else if (this.model.get('graph') === ('percent_loss')) { + var sql = 'WITH loss as (SELECT '; + + for(var y = 2001; y < 2012; y++) { + sql += 'y'+y+' as loss_y'+y+', '; + } + + sql += "y2012 as loss_y2012\ + FROM loss_gt_25\ + WHERE iso = '"+iso+"'), extent as (SELECT "; + + for(var y = 2001; y < 2012; y++) { + sql += 'y'+y+' as extent_y'+y+', '; + } + + sql += "y2012 as extent_y2012\ + FROM extent_gt_25\ + WHERE iso = '"+iso+"')"; + + sql += 'SELECT '; + + for(var y = 2001; y < 2012; y++) { + sql += 'loss_y'+y+'/extent_y'+y+' as percent_'+y+', '; + } + + sql += 'loss_y2012/extent_y2012 as percent_2012\ + FROM loss, extent'; + + d3.json('https://wri-01.cartodb.com/api/v2/sql?q='+encodeURIComponent(sql), function(json) { + var data = json.rows[0]; + + var data_ = []; + + _.each(data, function(val, key) { + data_.push({ + 'year': key.replace('y',''), + 'value': val*100 + }); + }); + + var y_scale = d3.scale.linear() + .domain([0, d3.max(data_, function(d) { return d.value; })]) + .range([height, 0]); + + var barWidth = width / data_.length; + + var bar = graph.selectAll('g') + .data(data_) + .enter().append('g') + .attr('transform', function(d, i) { return 'translate(' + (i * barWidth) + ', 0)'; }); + + bar.append('svg:rect') + .attr('class', 'bar') + .attr('y', function(d) { return y_scale(d.value); }) + .attr('height', function(d) { return height - y_scale(d.value); }) + .attr('width', barWidth - 1); + + }); + } else if (this.model.get('graph') === ('total_extent')) { + var sql = 'SELECT '; + + for(var y = 2001; y < 2012; y++) { + sql += 'loss.y'+y+' as loss_y'+y+', '; + } + + sql += 'loss.y2012 as loss_y2012, '; + + for(var y = 2001; y < 2012; y++) { + sql += 'extent.y'+y+' as extent_y'+y+', '; + } + + sql += "extent.y2012 as extent_y2012\ + FROM loss_gt_0 loss, extent_gt_25 extent\ + WHERE loss.iso = extent.iso\ + AND loss.iso = '"+iso+"'"; + + d3.json('https://wri-01.cartodb.com/api/v2/sql?q='+sql, function(json) { + + var graph2 = d3.select('.countries_list__minioverview_'+iso) + .append('div') + .attr('class', 'sibling') + .append('svg:svg') + .attr('width', width) + .attr('height', height); + + var data = json.rows[0]; + + var data_loss_ = [], + data_extent_ = []; + + _.each(data, function(val, key) { + if (key.indexOf('loss_y') != -1) { + data_loss_.push({ + 'year': key.split('_y')[1], + 'value': val + }); + } + + if (key.indexOf('extent_y') != -1) { + data_extent_.push({ + 'year': key.split('extent_y')[1], + 'value': val + }); + } + }); + + var y_scale_loss = d3.scale.linear() + .domain([0, d3.max(data_loss_, function(d) { return d.value; })]) + .range([height, 0]); + + var y_scale_extent = d3.scale.linear() + .domain([0, d3.max(data_extent_, function(d) { return d.value; })]) + .range([height, 0]); + + var barWidth_loss = width / data_loss_.length; + + var bar = graph.selectAll('g') + .data(data_loss_) + .enter() + .append('g') + .attr('transform', function(d, i) { return 'translate(' + (i * barWidth_loss) + ', 0)'; }); + + bar.append('svg:rect') + .attr('class', 'bar') + .attr('y', function(d) { return y_scale_loss(d.value); }) + .attr('height', function(d) { return height - y_scale_loss(d.value); }) + .attr('width', barWidth_loss - 1); + + var barWidth_extent = width / data_extent_.length; + + var bar2 = graph2.selectAll('g') + .data(data_extent_) + .enter() + .append('g') + .attr('transform', function(d, i) { return 'translate(' + (i * barWidth_extent) + ', 0)'; }); + + bar2.append('svg:rect') + .attr('class', 'bar extent') + .attr('y', function(d) { return y_scale_extent(d.value); }) + .attr('height', function(d) { return height - y_scale_extent(d.value); }) + .attr('width', barWidth_extent - 1); + }); + } + }, + + _drawYears: function() { + var markup_years = ''; + + for (var y = 2001; y<=2012; y += 1) { + var y_ = this.x_scale(y); + + if (y === 2001) { + y_ -= 25; + } else if (y === 2012) { + y_ -= 55; + } else { + y_ -= 40; + } + + markup_years += ''+y+''; + } + + this.$years.html(markup_years); + }, + + _drawGraph: function() { + var that = this; + + var w = this.w, + h = this.h, + vertical_m = this.vertical_m, + m = this.m, + x_scale = this.x_scale; + + var grid_scale = d3.scale.linear() + .range([vertical_m, h-vertical_m]) + .domain([1, 0]); + + d3.select('#chart').remove(); + + var svg = d3.select('.overview_graph__area') + .append('svg:svg') + .attr('id', 'chart') + .attr('width', w) + .attr('height', h); + + // grid + svg.selectAll('line.grid_h') + .data(grid_scale.ticks(4)) + .enter() + .append('line') + .attr({ + 'class': 'grid grid_h', + 'x1': 0, + 'x2': w, + 'y1': function(d, i) { return grid_scale(d); }, + 'y2': function(d, i) { return grid_scale(d); } + }); + + svg.selectAll('line.grid_v') + .data(x_scale.ticks(12)) + .enter() + .append('line') + .attr({ + 'class': 'grid grid_v', + 'y1': h, + 'y2': 0, + 'x1': function(d) { return x_scale(d); }, + 'x2': function(d) { return x_scale(d); } + }); + + var gradient = svg.append('svg:defs') + .append('svg:linearGradient') + .attr('id', 'gradient') + .attr('x1', '0%') + .attr('y1', '0%') + .attr('x2', '0%') + .attr('y2', '100%') + .attr('spreadMethod', 'pad'); + + gradient.append('svg:stop') + .attr('offset', '0%') + .attr('stop-color', '#CA46FF') + .attr('stop-opacity', .5); + + gradient.append('svg:stop') + .attr('offset', '100%') + .attr('stop-color', '#D24DFF') + .attr('stop-opacity', 1); + + if (this.model.get('graph') === 'total_loss') { + this._showYears(); + + svg.append('text') + .attr('class', 'axis') + .attr('id', 'axis_y') + .text('Mha') + .attr('x', -h/2) + .attr('y', 30) + .attr('transform', 'rotate(-90)'); + + var sql = 'SELECT '; + + for(var y = 2001; y < 2012; y++) { + sql += 'SUM(y'+y+') as y'+y+', ' + } + + sql += 'SUM(y2012) as y2012, (SELECT SUM(y2001_y2012)\ + FROM countries_gain) as gain\ + FROM loss_gt_0'; + + d3.json('https://wri-01.cartodb.com/api/v2/sql?q='+sql, function(error, json) { + var data = json.rows[0]; + + var data_ = [], + gain = null; + + _.each(data, function(val, key) { + if (key === 'gain') { + gain = val/12; + } else { + data_.push({ + 'year': key.replace('y',''), + 'value': val + }); + } + }); + + var y_scale = d3.scale.linear() + .range([vertical_m, h-vertical_m]) + .domain([d3.max(data_, function(d) { return d.value; }), 0]); + + // area + var area = d3.svg.area() + .x(function(d) { return x_scale(d.year); }) + .y0(h) + .y1(function(d) { return y_scale(d.value); }); + + svg.append('path') + .datum(data_) + .attr('class', 'area') + .attr('d', area) + .style('fill', 'url(#gradient)'); + + // circles + svg.selectAll('circle') + .data(data_) + .enter() + .append('svg:circle') + .attr('class', 'linedot') + .attr('cx', function(d) { + return x_scale(d.year); + }) + .attr('cy', function(d){ + return y_scale(d.value); + }) + .attr('r', 6) + .attr('name', function(d) { + return ''+d.year+''+formatNumber(parseFloat(d.value/1000000).toFixed(1))+' Mha'; + }) + .on('mouseover', function(d) { + that.tooltip.html($(this).attr('name')) + .style('visibility', 'visible') + .style('top', $(this).offset().top-100+'px') + .style('left', $(this).offset().left-$('.tooltip').width()/2-4+'px') + .attr('class', 'tooltip'); + + d3.select(this) + .transition() + .duration(100) + .attr('r', 7); + + // TODO: highlighting the legend + }) + .on('mouseout', function(d) { + that.tooltip.style('visibility', 'hidden'); + + d3.select(this) + .transition() + .duration(100) + .attr('r', 6); + + // TODO: highlighting the legend + }); + + var data_gain_ = [ + { + year: 2001, + value: gain + }, + { + year: 2012, + value: gain + } + ]; + + // line + svg.selectAll('line.overview_line') + .data([data_gain_[0]]) + .enter() + .append('line') + .attr({ + 'class': 'overview_line', + 'x1': m, + 'x2': w-m, + 'y1': function(d) { return y_scale(gain); }, + 'y2': function(d) { return y_scale(gain); } + }); + + svg.selectAll('circle.gain') + .data(data_gain_) + .enter() + .append('svg:circle') + .attr('class', 'linedot gain') + .attr('cx', function(d) { + return x_scale(d.year); + }) + .attr('cy', function(d){ + return y_scale(d.value); + }) + .attr('r', 6) + .attr('name', function(d) { + return '2001-2012'+formatNumber(parseFloat(d.value/1000000).toFixed(1))+' Mha'; + }) + .on('mouseover', function(d) { + that.tooltip.html($(this).attr('name')) + .style('visibility', 'visible') + .style('top', $(this).offset().top-100+'px') + .style('left', $(this).offset().left-$('.tooltip').width()/2-4+'px') + .attr('class', 'tooltip gain_tooltip'); + + d3.select(this) + .transition() + .duration(100) + .attr('r', 7); + + // TODO: highlighting the legend + }) + .on('mouseout', function(d) { + that.tooltip.style('visibility', 'hidden'); + + d3.select(this) + .transition() + .duration(100) + .attr('r', 6); + + // TODO: highlighting the legend + }); + }); + } else if (this.model.get('graph') === 'percent_loss') { + this._showYears(); + + svg.append('text') + .attr('class', 'axis') + .attr('id', 'axis_y') + .text('%') + .attr('x', -h/2) + .attr('y', 30) + .attr('transform', 'rotate(-90)'); + + var sql = 'WITH loss as (SELECT '; + + for(var y = 2001; y < 2012; y++) { + sql += 'SUM(y'+y+') as sum_loss_y'+y+', '; + } + + sql += 'SUM(y2012) as sum_loss_y2012\ + FROM loss_gt_25), extent as (SELECT '; + + for(var y = 2001; y < 2012; y++) { + sql += 'SUM(y'+y+') as sum_extent_y'+y+', '; + } + + sql += 'SUM(y2012) as sum_extent_y2012\ + FROM extent_gt_25)\ + SELECT '; + + for(var y = 2001; y < 2012; y++) { + sql += 'sum_loss_y'+y+'/sum_extent_y'+y+' as percent_loss_'+y+', '; + } + + sql += 'sum_loss_y2012/sum_extent_y2012 as percent_loss_2012, (SELECT SUM(y2001_y2012)/('; + + for(var y = 2001; y < 2012; y++) { + sql += 'sum_extent_y'+y+' + '; + } + + sql += 'sum_extent_y2012)\ + FROM countries_gain) as gain\ + FROM loss, extent'; + + d3.json('https://wri-01.cartodb.com/api/v2/sql?q='+encodeURIComponent(sql), function(json) { + var data = json.rows[0]; + + var data_ = [], + gain = null; + + _.each(data, function(val, key) { + if (key === 'gain') { + gain = val/12; + } else { + data_.push({ + 'year': key.replace('percent_loss_',''), + 'value': val + }); + } + }); + + var y_scale = grid_scale; + + // area + var area = d3.svg.area() + .x(function(d) { return x_scale(d.year); }) + .y0(h) + .y1(function(d) { return y_scale(d.value*100); }); + + svg.append('path') + .datum(data_) + .attr('class', 'area') + .attr('d', area) + .style('fill', 'url(#gradient)'); + + // circles + svg.selectAll('circle') + .data(data_) + .enter() + .append('svg:circle') + .attr('class', 'linedot') + .attr('cx', function(d) { + return x_scale(d.year); + }) + .attr('cy', function(d){ + return y_scale(d.value*100); + }) + .attr('r', 6) + .attr('name', function(d) { + return ''+d.year+''+parseFloat(d.value*100).toFixed(2)+' %'; + }) + .on('mouseover', function(d) { + that.tooltip.html($(this).attr('name')) + .style('visibility', 'visible') + .style('top', $(this).offset().top-100+'px') + .style('left', $(this).offset().left-$('.tooltip').width()/2-4+'px') + .attr('class', 'tooltip'); + + d3.select(this) + .transition() + .duration(100) + .attr('r', 7); + + // TODO: highlighting the legend + }) + .on('mouseout', function(d) { + that.tooltip.style('visibility', 'hidden'); + + d3.select(this) + .transition() + .duration(100) + .attr('r', 6); + + // TODO: highlighting the legend + }); + + var data_gain_ = [ + { + year: 2001, + value: gain + }, + { + year: 2012, + value: gain + } + ]; + + // line + svg.selectAll('line.overview_line') + .data([data_gain_[0]]) + .enter() + .append('line') + .attr({ + 'class': 'overview_line', + 'x1': m, + 'x2': w-m, + 'y1': function(d) { return y_scale(gain*100); }, + 'y2': function(d) { return y_scale(gain*100); } + }); + + // circles + svg.selectAll('circle.gain') + .data(data_gain_) + .enter() + .append('svg:circle') + .attr('class', 'linedot gain') + .attr('cx', function(d) { + return x_scale(d.year); + }) + .attr('cy', function(d){ + return y_scale(d.value*100); + }) + .attr('r', 6) + .attr('name', function(d) { + return '2001-2012'+parseFloat(d.value*100).toFixed(2)+' %'; + }) + .on('mouseover', function(d) { + that.tooltip.html($(this).attr('name')) + .style('visibility', 'visible') + .style('top', $(this).offset().top-100+'px') + .style('left', $(this).offset().left-$('.tooltip').width()/2-4+'px') + .attr('class', 'tooltip gain_tooltip'); + + d3.select(this) + .transition() + .duration(100) + .attr('r', 7); + + // TODO: highlighting the legend + }) + .on('mouseout', function(d) { + that.tooltip.style('visibility', 'hidden'); + + d3.select(this) + .transition() + .duration(100) + .attr('r', 6); + + // TODO: highlighting the legend + }); + }); + } else if (this.model.get('graph') === 'total_extent') { + this._showYears(); + + svg.append('text') + .attr('class', 'axis') + .attr('id', 'axis_y') + .text('Mha') + .attr('x', -h/2) + .attr('y', 30) + .attr('transform', 'rotate(-90)'); + + var gradient_extent = svg.append('svg:defs') + .append('svg:linearGradient') + .attr('id', 'gradient_extent') + .attr('x1', '0%') + .attr('y1', '0%') + .attr('x2', '0%') + .attr('y2', '100%') + .attr('spreadMethod', 'pad'); + + gradient_extent.append('svg:stop') + .attr('offset', '0%') + .attr('stop-color', '#98BD17') + .attr('stop-opacity', .5); + + gradient_extent.append('svg:stop') + .attr('offset', '100%') + .attr('stop-color', '#98BD17') + .attr('stop-opacity', 1); + + var sql = 'SELECT '; + + for(var y = 2001; y < 2012; y++) { + sql += 'SUM(loss.y'+y+') as loss_y'+y+', '; + } + + sql += 'SUM(loss.y2012) as loss_y2012, '; + + for(var y = 2001; y < 2012; y++) { + sql += 'SUM(extent.y'+y+') as extent_y'+y+', '; + } + + sql += 'SUM(extent.y2012) as extent_y2012\ + FROM loss_gt_25 loss, extent_gt_25 extent\ + WHERE loss.iso = extent.iso'; + + d3.json('https://wri-01.cartodb.com/api/v2/sql?q='+encodeURIComponent(sql), function(json) { + var data = json.rows[0]; + + var data_ = [], + data_loss_ = [], + data_extent_ = []; + + _.each(data, function(val, key) { + var year = key.split('_y')[1]; + + var obj = _.find(data_, function(obj) { return obj.year == year; }); + + if (obj === undefined) { + data_.push({ 'year': year }); + } + + if (key.indexOf('loss_y') != -1) { + data_loss_.push({ + 'year': key.split('_y')[1], + 'value': val + }); + } + + if (key.indexOf('extent_y') != -1) { + data_extent_.push({ + 'year': key.split('extent_y')[1], + 'value': val + }); + } + }); + + _.each(data_, function(val) { + var loss = _.find(data_loss_, function(obj) { return obj.year == val.year; }), + extent = _.find(data_extent_, function(obj) { return obj.year == val.year; }); + + _.extend(val, { 'loss': loss.value, 'extent': extent.value }); + }); + + var domain = [d3.max(data_, function(d) { return d.extent; }), 0]; + + var y_scale = d3.scale.linear() + .range([vertical_m, h-vertical_m]) + .domain(domain); + + // area + var area_loss = d3.svg.area() + .x(function(d) { return x_scale(d.year); }) + .y0(h) + .y1(function(d) { return y_scale(d.loss); }); + + var area_extent = d3.svg.area() + .x(function(d) { return x_scale(d.year); }) + .y0(function(d) { return y_scale(d.extent); }) + .y1(function(d) { return y_scale(d.loss); }); + + svg.append('path') + .datum(data_) + .attr('class', 'area') + .attr('d', area_loss) + .style('fill', 'url(#gradient)'); + + svg.append('path') + .datum(data_) + .attr('class', 'area') + .attr('d', area_extent) + .style('fill', 'url(#gradient_extent)'); + + // circles + svg.selectAll('circle') + .data(data_loss_) + .enter() + .append('svg:circle') + .attr('class', 'linedot') + .attr('cx', function(d) { + return x_scale(d.year); + }) + .attr('cy', function(d){ + return y_scale(d.value); + }) + .attr('r', 6) + .attr('name', function(d) { + return ''+d.year+''+formatNumber(parseFloat(d.value/1000000).toFixed(1))+' Mha'; + }) + .on('mouseover', function(d) { + that.tooltip.html($(this).attr('name')) + .style('visibility', 'visible') + .style('top', $(this).offset().top-100+'px') + .style('left', $(this).offset().left-$('.tooltip').width()/2-4+'px') + .attr('class', 'tooltip'); + + d3.select(this) + .transition() + .duration(100) + .attr('r', 7); + + // TODO: highlighting the legend + }) + .on('mouseout', function(d) { + that.tooltip.style('visibility', 'hidden'); + + d3.select(this) + .transition() + .duration(100) + .attr('r', 6); + + // TODO: highlighting the legend + }); + + svg.selectAll('circle.gain') + .data(data_extent_) + .enter() + .append('svg:circle') + .attr('class', 'linedot gain') + .attr('cx', function(d) { + return x_scale(d.year); + }) + .attr('cy', function(d){ + return y_scale(d.value); + }) + .attr('r', 6) + .attr('name', function(d) { + return ''+d.year+''+formatNumber(parseFloat(d.value/1000000).toFixed(1))+' Mha'; + }) + .on('mouseover', function(d) { + that.tooltip.html($(this).attr('name')) + .style('visibility', 'visible') + .style('top', $(this).offset().top-100+'px') + .style('left', $(this).offset().left-$('.tooltip').width()/2-4+'px') + .attr('class', 'tooltip gain_tooltip'); + + d3.select(this) + .transition() + .duration(100) + .attr('r', 7); + + // TODO: highlighting the legend + }) + .on('mouseout', function(d) { + that.tooltip.style('visibility', 'hidden'); + + d3.select(this) + .transition() + .duration(100) + .attr('r', 6); + + // TODO: highlighting the legend + }); + }); + } else if (this.model.get('graph') === 'ratio') { + this._hideYears(); + + svg.append('text') + .attr('class', 'axis light') + .attr('id', 'axis_y') + .text('Ratio of tree cover loss to gain 2001-2012') + .attr('x', -(h/2)-60) + .attr('y', 30) + .attr('transform', 'rotate(-90)'); + + svg.append('text') + .attr('class', 'axis light') + .attr('id', 'axis_ratio') + .text('1') + .attr('x', 25) + .attr('y', h-60); + + var shadow = svg.append('svg:defs') + .append('svg:filter') + .attr('id', 'shadow') + .attr('x', '0%') + .attr('y', '0%') + .attr('width', '200%') + .attr('height', '200%'); + + shadow.append('svg:feOffset') + .attr('result', 'offOut') + .attr('in', 'SourceAlpha') + .attr('dx', 0) + .attr('dy', 0); + + shadow.append('svg:feGaussianBlur') + .attr('result', 'blurOut') + .attr('in', 'offOut') + .attr('stdDeviation', 1); + + shadow.append('svg:feBlend') + .attr('in', 'SourceGraphic') + .attr('in2', 'blurOut') + .attr('mode', 'normal'); + + var sql = 'WITH loss as (SELECT iso, SUM('; + + for(var y = 2001; y < 2012; y++) { + sql += 'loss.y'+y+' + '; + } + + sql += ['loss.y2012) as sum_loss', + 'FROM loss_gt_50 loss', + 'GROUP BY iso), gain as ('].join(' '); + + sql += ['SELECT g.iso, SUM(y2001_y2012) as sum_gain', + 'FROM countries_gain g, loss_gt_50 loss', + 'WHERE loss.iso = g.iso', + 'GROUP BY g.iso), ratio as ('].join(' '); + + sql += ['SELECT c.iso, c.name, c.enabled, loss.sum_loss as loss,', + 'gain.sum_gain as gain, loss.sum_loss/gain.sum_gain as ratio', + 'FROM loss, gain, gfw2_countries c', + 'WHERE sum_gain IS NOT null', + 'AND NOT sum_gain = 0', + 'AND c.iso = gain.iso', + 'AND c.iso = loss.iso', + 'ORDER BY loss.sum_loss DESC', + 'LIMIT 50), extent as ('].join(' '); + + sql += ['SELECT extent.iso, SUM(extent.y2012) as extent', + 'FROM countries_extent extent', + 'GROUP BY extent.iso) '].join(' '); + + sql += ['SELECT *', + 'FROM ratio, extent', + 'WHERE ratio IS NOT null', + 'AND extent.iso = ratio.iso'].join(' '); + + d3.json('https://wri-01.cartodb.com/api/v2/sql?q='+encodeURIComponent(sql), function(json) { + var data = json.rows; + + var log_m = 50; + + var x_log_scale = d3.scale.log() + .range([m, w-m]) + .domain([d3.min(data, function(d) { return d.extent; }), d3.max(data, function(d) { return d.extent; })]); + + var y_log_scale = d3.scale.log() + .range([h-log_m, m]) + .domain([d3.min(data, function(d) { return d.ratio; }), d3.max(data, function(d) { return d.ratio; })]); + + var color_scale = d3.scale.linear() + .domain([d3.min(data, function(d) { return d.ratio; }), 1, 10, d3.max(data, function(d) { return d.ratio; })]) + .range(["#9ABF00", "#9ABF00", "#CA46FF", "#CA46FF"]); + + // line + svg.selectAll('line.linear_regression') + .data([1]) + .enter() + .append('line') + .attr({ + 'class': 'linear_regression', + 'x1': m, + 'x2': w-m, + 'y1': function(d) { return y_log_scale(d); }, + 'y2': function(d) { return y_log_scale(d); }, + "stroke-width": 1.3, + "stroke": "white", + "stroke-dasharray": "7,5" + }); + + // circles w/ magic numbers :( + var circle_attr = { + 'cx': function(d) { return x_log_scale(d.extent) }, + 'cy': function(d) { return y_log_scale(d.ratio) }, + 'r': 5, + 'name': function(d) { return d.name; }, + 'class': function(d) { return d.enabled ? 'ball ball_link' : 'ball ball_nolink'; } + }; + + var data_ = [], + data_link_ = [], + exclude = []; + + _.each(data, function(row) { + if (!_.contains(exclude, row.name)) { + if (row.enabled === true) { + data_link_.push(row); + } else { + data_.push(row); + } + } + }); + + var circles_link = svg.selectAll('circle.ball_link') + .data(data_link_) + .enter() + .append('a') + .attr('xlink:href', function(d) { return '/country/' + d.iso}) + .append('svg:circle') + .attr(circle_attr) + .style('fill', function(d) { + return color_scale(d.ratio); + }) + .style('filter', 'url(#shadow)') + .on('mouseover', function() { + d3.select(d3.event.target) + .transition() + .attr('r', 7) + .style('opacity', 1); + + var t = $(this).offset().top - 80, + l = $(this).offset().left, + r = $(this).attr('r'), + tip = $('.tooltip').width()/2; + + that.tooltip.html($(this).attr('name')) + .style('visibility', 'visible') + .style('top', parseInt(t, 10)+'px') + .style('left', parseInt(l, 10)+parseInt(r, 10)-parseInt(tip, 10)-10+'px') + .attr('class', 'tooltip gain_tooltip'); + }) + .on('mouseenter', function() { + d3.select(d3.event.target) + .transition() + .attr('r', 7) + .style('opacity', 1); + + var t = $(this).offset().top - 80, + l = $(this).offset().left, + r = $(this).attr('r'), + tip = $('.tooltip').width()/2; + + that.tooltip.html($(this).attr('name')) + .style('visibility', 'visible') + .style('top', parseInt(t, 10)+'px') + .style('left', parseInt(l, 10)+parseInt(r, 10)-parseInt(tip, 10)+'px') + .attr('class', 'tooltip gain_tooltip'); + }) + .on('mouseout', function() { + d3.select(d3.event.target) + .transition() + .attr('r', 5) + .style('opacity', .8); + + that.tooltip.style('visibility', 'hidden'); + }); + + var circles = svg.selectAll('circle.ball_nolink') + .data(data_) + .enter() + .append('svg:circle') + .attr(circle_attr) + .style('fill', function(d) { + return color_scale(d.ratio); + }) + .style('filter', 'url(#shadow)') + .on('mouseover', function() { + d3.select(d3.event.target) + .transition() + .attr('r', 7) + .style('opacity', 1); + + var t = $(this).offset().top - 80, + l = $(this).offset().left, + r = $(this).attr('r'), + tip = $('.tooltip').width()/2; + + that.tooltip.html($(this).attr('name')) + .style('visibility', 'visible') + .style('top', parseInt(t, 10)+'px') + .style('left', parseInt(l, 10)+parseInt(r, 10)-parseInt(tip, 10)-10+'px') + .attr('class', 'tooltip gain_tooltip'); + }) + .on('mouseenter', function() { + d3.select(d3.event.target) + .transition() + .attr('r', 7) + .style('opacity', 1); + + var t = $(this).offset().top - 80, + l = $(this).offset().left, + r = $(this).attr('r'), + tip = $('.tooltip').width()/2; + + that.tooltip.html($(this).attr('name')) + .style('visibility', 'visible') + .style('top', parseInt(t, 10)+'px') + .style('left', parseInt(l, 10)+parseInt(r, 10)-parseInt(tip, 10)+'px') + .attr('class', 'tooltip gain_tooltip'); + }) + .on('mouseout', function() { + d3.select(d3.event.target) + .transition() + .attr('r', 5) + .style('opacity', .8); + + that.tooltip.style('visibility', 'hidden'); + }); + }); + } else if (this.model.get('graph') === 'domains') { + this._showYears(); + + var sql = 'SELECT name, '; + + for(var y = 2001; y < 2012; y++) { + sql += 'y'+y+', ' + } + + sql += 'y2012, GREATEST(' + + for(var y = 2001; y < 2012; y++) { + sql += 'y'+y+', ' + } + + sql += 'y2012) as max\ + FROM countries_domains'; + + d3.json('https://wri-01.cartodb.com/api/v2/sql?q='+sql, function(error, json) { + var data = json.rows; + + var r_scale = d3.scale.linear() + .range([5, 30]) // max ball radius + .domain([0, d3.max(data, function(d) { return d.max; })]) + + for(var j = 0; j < data.length; j++) { + var data_ = [], + domain = ''; + + _.each(data[j], function(val, key) { + if (key !== 'max') { + if (key === 'name') { + domain = val.toLowerCase(); + } else { + data_.push({ + 'year': key.replace('y',''), + 'value': val + }); + } + } + }); + + svg.append('text') + .attr('class', 'label') + .attr('id', 'label_'+domain) + .text(domain) + .attr('x', function() { + var l = x_scale(2002) - $(this).width()/2; + + return l; + }) + .attr('y', (h/5)*(j+.6)); + + var circle_attr = { + 'cx': function(d, i) { return x_scale(2001 + i); }, + 'cy': function(d) { return (h/5)*(j+1); }, + 'r': function(d) { return r_scale(d.value); }, + 'class': function(d) { return 'ball'; } + }; + + svg.selectAll('circle.domain_'+domain) + .data(data_) + .enter() + .append('svg:circle') + .attr(circle_attr) + .attr('data-slug', domain) + .attr('name', function(d) { + return ''+d.year+''+formatNumber(parseFloat(d.value/1000000).toFixed(1))+' Mha'; + }) + .style('fill', function(d) { return config.GRAPHCOLORS[domain]; }) + .on('mouseover', function() { + d3.select(d3.event.target) + .transition() + .attr('r', function(d) { return circle_attr.r(d) + 2; }) + .style('opacity', 1); + + var t = $(this).offset().top - 100, + l = $(this).offset().left, + r = $(this).attr('r'), + tip = $('.tooltip').width()/2, + slug = $(this).attr('data-slug'); + + that.tooltip.html($(this).attr('name')) + .style('visibility', 'visible') + .style('top', parseInt(t, 10)+'px') + .style('left', parseInt(l, 10)+parseInt(r, 10)-parseInt(tip, 10)-10+'px') + .attr('class', 'tooltip') + .attr('data-slug', 'tooltip') + .style('color', function() { + if (slug === 'subtropical') { + return '#FFC926' + } else { + return config.GRAPHCOLORS[slug]; + } + }); + }) + .on('mouseenter', function() { + d3.select(d3.event.target) + .transition() + .attr('r', function(d) { return circle_attr.r(d) + 2; }) + .style('opacity', 1); + + var t = $(this).offset().top - 80, + l = $(this).offset().left, + r = $(this).attr('r'), + tip = $('.tooltip').width()/2, + slug = $(this).attr('data-slug'); + + that.tooltip.html($(this).attr('name')) + .style('visibility', 'visible') + .style('top', parseInt(t, 10)+'px') + .style('left', parseInt(l, 10)+parseInt(r, 10)-parseInt(tip, 10)-10+'px') + .attr('class', 'tooltip') + .attr('data-slug', 'tooltip') + .style('color', function() { + if (domain === 'subtropical') { return config.GRAPHCOLORS[domain]; } + }); + }) + .on('mouseout', function() { + d3.select(d3.event.target) + .transition() + .attr('r', function(d) { return circle_attr.r(d); }) + .style('opacity', .8); + + that.tooltip + .style('color', '') + .style('visibility', 'hidden'); + }); + } + }); + } + } +}); \ No newline at end of file diff --git a/app/assets/javascripts/countries/views/show.js b/app/assets/javascripts/countries/views/show.js new file mode 100644 index 0000000000..0c857a039d --- /dev/null +++ b/app/assets/javascripts/countries/views/show.js @@ -0,0 +1,482 @@ +gfw.ui.view.CountriesShow = cdb.core.View.extend({ + el: $('.country-show'), + + events: { + 'click .info': '_openSource', + 'click .forma_dropdown-link': '_openDropdown', + }, + + initialize: function() { + var self = this; + _.bindAll(this, '_positionScroll'); + + // Cache + this.$nav = this.$('.country-nav'); + this.$indepth = this.$('.country-indepth'); + + // Models + this.country = new gfw.ui.model.Country({ iso: this.options.iso }); + + // Initialize modules + this.headerView = new gfw.ui.view.CountryHeader({country: this.country}); + this._stickynav(); + this._initSource(); + this._drawTenure(); + this._drawForestsType(); + this._drawFormaAlerts(); + this._initFormaDropdown(); + this._initShare(); + }, + + _initShare: function() { + Share = new gfw.ui.view.Share(); + //this.$el.find('.country-show .inner').append(Share.render()); + }, + + _initSource: function() { + this.sourceWindow = new gfw.ui.view.SourceWindow(); + this.$el.append(this.sourceWindow.render()); + }, + + _openSource: function(e) { + e.preventDefault(); + + var source = $(e.target).closest('.info').attr('data-source'); + + // ga('send', 'event', 'SourceWindow', 'Open', source); + this.sourceWindow.show(source).addScroll(); + }, + + _initFormaDropdown: function() { + $('.forma_dropdown-link').qtip({ + show: 'click', + hide: { + event: 'click unfocus' + }, + content: { + text: $('.forma_dropdown-menu') + }, + position: { + my: 'bottom right', + at: 'top right', + target: $('.forma_dropdown-link'), + adjust: { + x: -10 + } + }, + style: { + tip: { + corner: 'bottom right', + mimic: 'bottom center', + border: 1, + width: 10, + height: 6 + } + } + }); + }, + + _positionScroll: function() { + var h_min = $('.country-alerts').offset().top - 48, + h_max = $('.country-conventions').offset().top - 46; + + if ($(window).scrollTop() > (h_min) && $(window).scrollTop() < h_max) { + // fixed + this.$nav.css({ + position: 'fixed', + top: 0 + }); + this.$nav.addClass('fixed'); + } else if ($(window).scrollTop() >= h_max) { + // dissapear + this.$nav.css({ + position: 'absolute', + top: h_max - h_min + }); + } else { + // default + this.$nav.css({ + position: 'absolute', + top: 0 + }); + this.$nav.removeClass('fixed'); + } + }, + + _stickynav: function() { + this._positionScroll(); + + $.scrollIt({ + upKey: null, + downKey: null, + easing: 'linear', + scrollTime: 400, + activeClass: 'active', + onPageChange: null, + topOffset: - 48 + }); + + $(window).scroll(this._positionScroll); + }, + + _drawTenure: function() { + var sql = ['SELECT tenure_government, tenure_owned, tenure_owned_individuals,', + 'tenure_reserved, GREATEST(tenure_government, tenure_owned,', + 'tenure_owned_individuals,', + 'tenure_owned_individuals,', + 'tenure_reserved) as max', + "FROM gfw2_countries WHERE iso = '" + this.country.get('iso') + "'"].join(' '); + + d3.json('https://wri-01.cartodb.com/api/v2/sql?q='+ sql, function(json) { + var data = json.rows[0], + h = 0; + + var x_extent = [0, data.max], + x_scale = d3.scale.linear() + .range([0, 500]) + .domain(x_extent); + + var origins = [], + aggr = 0, + klass = ['one', 'two', 'three', 'four'] + + var tenures = [ + { + name: 'Public lands administered by the government', + percent: data.tenure_government + }, + { + name: 'Public lands reserved for communities and indigenous groups', + percent: data.tenure_reserved + }, + { + name: 'Private lands owned by communities and indigenous groups', + percent: data.tenure_owned + }, + { + name: 'Private lands owned by firms and individuals', + percent: data.tenure_owned_individuals + } + ]; + + var tenures_ord = []; + + _.each(tenures, function(tenure, i) { + if (tenure['percent'] !== null && tenure['percent'] !== 0) { + h += 50; + + tenures_ord.push({ + name: tenure['name'], + percent: tenure['percent'] + }); + } + }); + + if (tenures_ord.length === 0) { + $('.country-tenure .coming-soon').show(); + return; + } + + var svg = d3.select('.country-tenure .line-graph') + .append('svg') + .attr('width', 600) + .attr('height', h); + + // add lines + svg.selectAll('rect') + .data(tenures_ord) + .enter() + .append('rect') + .attr('class', function(d, i) { + return klass[i]; + }) + .attr('x', function() { + return x_scale(0); + }) + .attr('y', function(d, i) { + return 25 + (50 * i); + }) + .attr('width', function(d) { + return x_scale(d['percent']); + }) + .attr('height', 4) + .attr('rx', 2) + .attr('ry', 2); + + // add balls + svg.selectAll('circle') + .data(tenures_ord) + .enter() + .append('svg:circle') + .attr('class', function(d, i) { + return klass[i]; + }) + .attr('cx', function(d, i) { + return x_scale(d['percent']); + }) + .attr('cy', function(d, i) { + return 27 + (50 * i); + }) + .attr('r', 5); + + // add values + svg.selectAll('.units') + .data(tenures_ord) + .enter() + .append('text') + .text(function(d) { + return d['percent']/1000000 + 'Mha'; + }) + .attr('class', function(d, i) { + return 'units ' + klass[i]; + }) + .attr('x', function(d, i) { + return x_scale(d['percent'])+10; + }) + .attr('y', function(d, i) { + return 31 + (50 * i); + }); + + // add legend + svg.selectAll('.legend') + .data(tenures_ord) + .enter() + .append('text') + .text(function(d) { + return d['name']; + }) + .attr('class', function(d, i) { + return 'legend ' + klass[i]; + }) + .attr('x', 0) + .attr('y', function(d, i) { + return 15 + (50 * i); + }); + }); + }, + + _drawForestsType: function() { + var sql = ["SELECT unnest(array['forest_regenerated', 'forest_primary', 'forest_planted'])", + 'AS type, unnest(array[COALESCE(forest_regenerated, 0),', + 'COALESCE(forest_primary, 0),', + 'COALESCE(forest_planted, 0)])', + 'AS percent', + 'FROM gfw2_countries', + "WHERE iso = '" + this.country.get('iso') + "'"].join(' '); + + d3.json('https://wri-01.cartodb.com/api/v2/sql?q=' + sql, function(json) { + var data = _.pluck(json.rows, 'percent'), + sumData = _.reduce(data, function(memo, num){ return memo + num; }, 0), + $countryForestType = $('.country-forests-type'); + + if (sumData === 0) { + $countryForestType.find('.coming-soon').show(); + return; + } + + if (sumData !== 100) { + data[2] = (100 - (data[0] + data[1])); + } + + $countryForestType.find('.forest-type-legends').show(); + + var width = 225, + height = 225, + radius = Math.min(width, height) / 2, + colors = ['#819515', '#A1BA42', '#DDDDDD'], + labelColors = ['white', 'white', '#555']; + + var pie = d3.layout.pie() + .sort(null); + + var arc = d3.svg.arc() // create elements for using arc data + .innerRadius(radius - 67) + .outerRadius(radius) + + var svg = d3.select(".forests-type-graph") + .append("svg") + .attr("width", width) + .attr("height", height) + .append("g") + .attr("transform", "translate(" + width / 2 + "," + height / 2 + ")"); + + var path = svg.selectAll("path") + .data(pie(data)); + + path.enter().append("path") + .attr("fill", function(d, i) { return colors[i]; }) + .attr("d", arc); + + path.enter().append('text') + .attr('transform', function(d) { var c = arc.centroid(d); return 'translate(' + (c[0]-12) + ',' + (c[1]+8) + ')'}) + .text(function(d) { + if (d.data > 0) return d.data + '%' + }) + .attr('fill', function(d, i) { return labelColors[i]; } ) + .style('font-size', '13px'); + }); + }, + + _drawFormaAlerts: function() { + var that = this; + + var $graph = this.$('.forma-graph'), + $tooltip = this.$('.graph-tooltip') + $amount = $tooltip.find('.graph-amount'), + $date = $tooltip.find('.graph-date'), + $comingSoonContent = this.$('#comingSoonContent'), + $formaAlertsContent = this.$('#formaAlertsContent'), + $formaAlertsTitle = this.$('#formaAlertsTitle'); + + // Dimensions variables + var width = 500, + height = 156, + h = 136, // maxHeight + radius = width / 2, + gridLinesCount = 7; + + // Init graph + var graph = d3.select('.forma-graph') + .append('svg:svg') + .attr('class', 'line') + .attr('width', width) + .attr('height', height); + + // Add dashed lines grid + var gridLineY = height; + for (var i = 0; i < gridLinesCount; i++) { + graph.append('svg:line') + .attr('x1', 0) + .attr('y1', gridLineY) + .attr('x2', width) + .attr('y2', gridLineY) + .style('stroke-dasharray', ('2, 3')) + .style('stroke', function() { return (i == 0) ? '#333' : '#CCC'; } ); + + gridLineY -= height/(gridLinesCount-1); + }; + + // Render forma graph + var sql = ["SELECT date_trunc('month', date) as date, COUNT(*) as alerts", + 'FROM forma_api', + "WHERE iso = '" + this.country.get('iso') + "'", + "GROUP BY date_trunc('month', date)", + "ORDER BY date_trunc('month', date) ASC"].join(' '); + + d3.json('https://wri-01.cartodb.com/api/v2/sql?q='+sql, function(json) { + if (json && json.rows.length > 0) { + var data = json.rows.slice(1, json.rows.length); + + var lastMonth = data[data.length - 1]; + $formaAlertsTitle.find('.amount').text(formatNumber(lastMonth.alerts)); + $formaAlertsTitle.find('.month').text(config.MONTHNAMES[new Date(lastMonth.date).getUTCMonth()]) + + $formaAlertsContent.show(); + } else { + $comingSoonContent.show(); + return; + }; + + var x_scale = d3.scale.linear() + .domain([0, data.length - 1]) + .range([0, width - 40]); + + var max = d3.max(data, function(d) { return parseFloat(d.alerts); }); + if (max === d3.min(data, function(d) { return parseFloat(d.alerts); })) { + h = h/2; + } + + var y_scale = d3.scale.linear() + .domain([0, max]) + .range([0, h]); + + var line = d3.svg.line() + .x(function(d, i) { return x_scale(i); }) + .y(function(d, i) { return h - y_scale(d.alerts); }) + .interpolate("monotone"); + + var marginTop = 20, + marginLeft = 20; + + var cx = width - 40 + marginLeft; + var cy = h - y_scale(data[data.length - 1].alerts) + marginTop; + + var tooltip = d3.select('.forma-graph') + .append('div') + .attr('class', 'graph-tooltip') + .style('visibility', 'hidden') + + var amount = tooltip + .append('div') + .attr('class', 'graph-amount') + .text('21,123') + + tooltip + .append('div') + .attr('class', 'graph-date') + .text('Alerts in') + + var tooltipDate = tooltip.select('.graph-date') + .append('div') + .attr('class', 'date') + .text('November 2012'); + + graph.append('svg:path') + .attr('transform', 'translate(' + marginLeft + ',' + marginTop + ')') + .attr('d', line(data)); + + var positioner = graph.append('svg:line') + .attr('x1', 0) + .attr('y1', 0) + .attr('x2', 0) + .attr('y2', height) + .style('visibility', 'hidden') + .style('stroke', '#aaa'); + + var marker = graph.append('svg:circle') + .attr('class', 'forma-marker') + .attr('cx', cx) + .attr('cy', cy) + .attr('r', 5); + + graph + .on("mouseout", function() { + positioner.style("visibility", "hidden"); + tooltip.style("visibility", "hidden"); + }) + .on("mouseover", function() { + positioner.style("visibility", "visible"); + tooltip.style("visibility", "visible"); + }) + .on('mousemove', function(d) { + var index = Math.round(x_scale.invert(d3.mouse(this)[0])); + + if (data[index]) { + var cx = d3.mouse(this)[0] + marginLeft, + cy = h - y_scale(data[index].alerts) + marginTop, + date = new Date(data[index].date), + formattedDate = config.MONTHNAMES[date.getUTCMonth()] + ' ' + date.getUTCFullYear(); + + marker + .attr('cx', cx) + .attr('cy', cy); + + positioner + .attr('x1', d3.mouse(this)[0] + marginLeft) + .attr('x2', d3.mouse(this)[0] + marginLeft); + + amount.text(formatNumber(data[index].alerts)); + tooltipDate.text(formattedDate); + tooltip.style("top", "-20px").style("left", (cx - 162) + "px"); + } + + }); + + }); + }, + + _openDropdown: function(e) { + e.preventDefault(); + }, + +}); \ No newline at end of file diff --git a/app/assets/javascripts/embed_countries.js b/app/assets/javascripts/embed_countries.js index 7a83b0c6b2..3c6fa6bcd9 100644 --- a/app/assets/javascripts/embed_countries.js +++ b/app/assets/javascripts/embed_countries.js @@ -15,7 +15,7 @@ gfw.ui.view.CountriesEmbedOverview = cdb.core.View.extend({ el: document.body, events: { - 'click .graph_tab': '_updateGraph', + 'click .graph_tab': '_updateGraph' }, initialize: function() { diff --git a/app/assets/stylesheets/application.scss b/app/assets/stylesheets/application.scss index d6b57c6063..144729b2e9 100644 --- a/app/assets/stylesheets/application.scss +++ b/app/assets/stylesheets/application.scss @@ -411,6 +411,20 @@ strong { #tpglt-nav-wrapper { padding-top: 15px; + min-height: 28px; + width: 100%; + background-color: #e6e6e6; + border-bottom: 1px solid #9b9b9b; + z-index: 1001; + font-size: 12px; + color: #747474; + + div:first-child { + margin: 0 auto; + position: relative; + padding: 0 10px; + max-width: 960px; + } a { color: #333; } } diff --git a/app/assets/stylesheets/countries.scss b/app/assets/stylesheets/countries.scss index 84a63cc568..56420f851e 100644 --- a/app/assets/stylesheets/countries.scss +++ b/app/assets/stylesheets/countries.scss @@ -1,16 +1,32 @@ -@import 'compass/css3/box-shadow'; -@import 'compass/css3/border-radius'; -@import 'compass/css3/opacity'; -@import 'compass/css3/background-size'; -@import 'compass/css3/images'; -@import 'compass/css3/text-shadow'; -@import 'compass/css3/inline-block'; - -@import 'fonts'; -@import 'helpers'; - -@import 'country-icons/*.png'; -/**/ +$primary-color: #9FBA2B; +$default-color: #463F52; +$font-light: "fira_sans_otlight", Georgia, sans-serif; +$font-regular: "fira_sans_otregular", Georgia, sans-serif; +$font-medium: "fira_sans_otmedium", Georgia, sans-serif; + +// Compass +@import "compass/css3/box-shadow", + "compass/css3/box-sizing", + "compass/css3/border-radius", + "compass/css3/opacity", + "compass/css3/background-size", + "compass/css3/images", + "compass/css3/text-shadow", + "compass/css3/inline-block", + "compass/utilities/sprites"; + +// Global +@import "fonts", + "helpers"; + +// Icons +@import "country-icons/*.png"; +@include all-country-icons-sprites; + +// Countries +@import "countries/index", + "countries/show", + "countries/overview"; .countries { &.index .content { @@ -80,1575 +96,43 @@ &.dark { @include country-icons-sprite(country_info_dark); } } - -/* =Index ------------------------------------------------ */ - -.countries_block { - height: 120px; - margin-top: -10px; - background: - image-url('backgrounds/bkg_countries_block_up.png') no-repeat 0 center, - image-url('backgrounds/bkg_countries_block_down.png') repeat-x; - margin-bottom: 40px; - - .header-title { - font-size: 29px; - line-height: 120px; - color: #fff; - - a:hover { - text-decoration: underline; - } - } - span{ - color: darken($cGreen, 5%); - } -} - -.country { - float: left; - margin: 0 0 -1px -1px; - font-size: 15px; - line-height: 18px; - text-align: center; - color: #999; - - a { - position: relative; - display: block; - width: 199px; - height: 230px; - padding: 20px; - border: 1px solid #E5E5E5; - background: #fff; - - &:hover { - z-index: 1000; - margin: -10px; - padding: 30px; - @include box-shadow(0 0 5px rgba(#000, .1)); - - .country_main, - .country_alt { - fill: $cGreen; - } - - .country-content { - position: absolute; - top: 200px; left: 30px; right: 30px; - } - - svg { - top: 30px; - } - } - } - - .country-content { - position: absolute; - top: 190px; left: 20px; - right: 20px; - - span { - display: block; - color: #999; - font-size: 15px; - line-height: 1.1; - } - } - - h3 { - display: block; - margin-bottom: 5px; - font-size: 29px; - line-height: 1.1; - @extend .serif; - color: #666; - - &.small { - font-size: 19px; - } - } - - strong { - font-weight: bold; - } - - svg { - position: absolute; - top: 20px; left: 50%; - margin-left: -75px; - } -} - - -/* =Show ------------------------------------------------ */ - -.country_graphs { - padding-top: 0; - - .columns { - padding: 14px 0 57px; - } - .share_control { - position: relative; - display: block; - width: 38px; - height: 38px; - left: 960px; - top: -38px; - @include country-icons-sprite(share_button); - - &:hover { @include country-icons-sprite(share_button_hover); } - &:active { @include country-icons-sprite(share_button_active); } - } -} - -.graph { - height: 266px; - - &.map { - .frame, - .info { - background: #444; - } - - .action { - border-color: #444; - @include country-icons-sprite(country_action); - background-color: #fff; - } - } - - &.forest_loss { - .frame, - .info { - background: #FFC926; - } - - .action { - border-color: #FFC926; - @include country-icons-sprite(forest_loss_action); - background-color: #fff; - } - - .graph-title, - .graph-amount { - color: #FFC926; - } - } - - .frame { - position: absolute; - top: 0; left: 0; - height: 100%; - width: 100%; - background: #FF4D4D; - @include border-radius(134px); - } - - &.ghost { - @include opacity(.5); - - .action { - cursor: default; - } - } - - .coming_soon { - display: none; - position: absolute; - z-index: 10; - top: 50%; left: 0; - width: 206px; - padding: 0 30px; - margin-top: -9px; - font-weight: bold; - font-size: 17px; - text-transform: uppercase; - text-align: center; - color: #111; - } - - .info { - position: absolute; - z-index: 10; - top: -14px; left: 50%; - border: 2px solid #fff; - margin-left: -14px; - width: 24px; - font-size: 14px; - line-height: 24px; - @extend .serif; - color: #fff; - text-align: center; - background: #FF4D4D; - @include border-radius(14px); - } - - .action { - position: absolute; - z-index: 10; - bottom: -20px; left: 50%; - border: 2px solid #FF4D4D; - margin-left: -20px; - width: 36px; - height: 36px; - @include country-icons-sprite(action); - background-color: #fff; - @include border-radius(20px); - - &.disabled { - cursor: default; - - &:after { - content: ''; - position: absolute; - top: -2px; left: -2px; - display: block; - width: 40px; - height: 40px; - background: #fff; - @include opacity(.5); - } - } - } - - img , - .frame_bkg { - position: absolute; - top: 5px; left: 5px; - height: 256px; - width: 256px; - @include border-radius(129px); - } - - .frame_bkg { - background: #fff; - } - - path { - stroke: #FF4D4D; - stroke-width: 4px; - fill: none; - } -} - -.forma_marker { - stroke: #fff; - stroke-width: 2px; - fill: #FF4D4D; -} - -.graph-title { - position: absolute; - z-index: 10; - top: 42px; left: 0; - width: 100%; - color: #FF4D4D; - font-size: 18px; - @extend .serif; - text-align: center; -} - -.graph-subtitle { - position: absolute; - z-index: 10; - top: 62px; left: 0; - width: 100%; - font-size: 13px; - @extend .serif; - text-align: center; - color: #666; - text-decoration: underline; - - span, - a { - line-height: 1.4; - background: #fff; - padding: 0 5px; - } - - a { - color: #666; - - &:after { - content: ''; - position: absolute; - top: 50%; - margin: -1px 0 0 3px; - border-color: #666 rgba(#000, 0); - border-width: 4px 4px 0 4px; - border-style: solid; - } - } -} - -.graph-amount { - position: absolute; - z-index: 10; - bottom: 57px; left: 0; - width: 100%; - font-size: 27px; - @extend .serif; - color: #FF4D4D; - text-align: center; - - span { - display: inline-block; - line-height: .8; - padding: 0 5px; - background: #fff; - } -} - -.graph-action-subtitle, -.graph-date { - position: absolute; - z-index: 100; - font-size: 13px; - width: 100%; - @extend .serif; - text-align: center; - color: #333; -} - -.graph-date { - bottom: 37px; left: 0; -} - -.graph-action-subtitle { - bottom: -50px; left: 0; -} - -.hansen_dropdown-menu, -.forma_dropdown-menu { - display: none; -} - -.bar { - fill: #FFC926; - shape-rendering: "crispEdges"; - @include opacity(.5); - - &.last { - @include opacity(1); - } -} - -.bar-container { - position: relative; - height: 48px; -} - -.bar-inner { - position: absolute; - z-index: 10; - top: 0; -} - -.country_indepth { - height: 48px; - width: 100%; - background: #ccc; - - p { - font-size: 17px; - line-height: 48px; - @extend .serif; - text-align: center; - color: #555; - } - - a { - text-decoration: underline; - color: #333; - - &:hover { - color: darken(#333, 5%); - } - } - - strong { - font-weight: bold; - } -} - -.country_menu { - height: 48px; - width: 100%; - background: #242424; - - li { - position: relative; - float: left; - width: 190px; - text-align: center; - border-left: 1px solid #4E4E4E; - - &:last-child { - border-right: 1px solid #4E4E4E; - } - - &.disabled { - @include opacity(.5); - @include pointer-events(none); - } - } - - a { - display: block; - color: #BEBEBE; - font-size: 17px; - line-height: 48px; - @extend .serif; - text-decoration: none; - - &:hover { - color: #fff; - } - - &.active { - color: #fff; - - &:after { - content: ''; - position: absolute; - bottom: 0; left: 50%; - margin-left: -3px; - border: 6px solid transparent; - border-bottom-color: #f5f5f5; - } - } - } -} - -.section-title { - margin-bottom: 40px; - font-size: 45px; - line-height: 1.1; - @extend .serif; - color: #444; - - .info i { top: -12px; } - - span { - color: $cGreen; - } -} - -.section-subtitle { - margin-bottom: 10px; - font-size: 17px; - @extend .serif; - color: #888; - - .info { - float: left; - margin-left: -30px; - margin-top: -3px; - } -} - -.country_state { - background-position: 48px 0; - background-image: image-url('backgrounds/bkg_country.png'); - - .inner { - padding-left: 370px; - width: 600px; - } - - .country_alt { - line-height: 1.4; - } -} - -.country-path { - position: relative; - float: left; - margin-left: -360px; - height: 302px; - - svg { - position: absolute; - } -} - -.country_main { fill: #444; } -.country_alt { fill: #999; } - -.country_state-title { - padding-top: 80px; -} - -.forest_type, -.forest_tenure { - display: none; -} - -.forest_type.tall { - padding-top: 80px; -} - -.line-graph { - font-weight: bold; - text-transform: uppercase; - - circle { - stroke-width: 2px; - stroke: #f5f5f5; - } - - text { - @extend .sans-serif; - font-size: 11px; - } - - .one { - fill: #75B22E; - } - - .two { - fill: #AAC700; - } - - .three { - fill: #AC0; - } - - .four { - fill: #FFD24D; - } -} - -.country_alt { - margin-top: 30px; - font-style: italic; - @extend .serif; -} - -.man_list { - margin-bottom: 10px; - color: #ccc; -} - -section.country_people { - background-position: right 0; - background-image: image-url('backgrounds/bkg_country.png'); - - .gross_value.narrow { - float: left; - width: 600px; - } - - .employment { - float: left; - width: 460px; - - &.short { - width: 300px; - margin-left: 60px; - } - - .section-title { - @extend .clearfix; - margin-bottom: 10px; - - div, - span { - float: left; - } - - span { - width: 250px; - margin: 10px 0 0 10px; - font-size: 13px; - line-height: 1.4; - @extend .sans-serif; - text-transform: uppercase; - color: #444; - } - } - } -} - -.country_indepth { - height: 48px; - width: 100%; - background: #ccc; - - p { - font-size: 17px; - line-height: 48px; - @extend .serif; - text-align: center; - color: #555; - } - - a { - color: #333; - text-decoration: underline; - - &:hover { - color: darken(#333, 5%); - } - } - - strong { - font-weight: bold; - } -} - -section.country_laws { - background-position: 48px 0; - background-image: image-url('backgrounds/bkg_country.png'); +.btn-rdn { + padding: 8px 15px 5px 15px; + border: 2px solid #D6E681; + color: #545454; font-size: 15px; - line-height: 1.6; - - a { - color: #555; - - &:hover { - text-decoration: underline; - } - - &.people-link:hover { - text-decoration: none; - } - } -} - -.people-link { display: inline-block; - border: 2px solid rgba(#ACCD00, .5); - line-height: 30px; - padding: 0 15px; - margin-top: 10px; - font-size: 15px; text-decoration: none; - @include border-radius(18px); - color: #555; + margin-top: 10px; + -webkit-border-radius: 18px; + -moz-border-radius: 18px; + -ms-border-radius: 18px; + -o-border-radius: 18px; + border-radius: 18px; &:hover { border: 2px solid #ACCD00; color: #444; } -} - -section.country_climate { - padding: 0; - - .inner { - padding: 0; - margin: 0 auto; - } - ul { - margin-right: -50px; + &.btn-default { } - li { - float: left; - width: 418px; - padding: 50px 35px 50px 0; - - &.wide { - padding-right: 0; - width: 100%; - } - - &.last { - background: #444; - text-align: right; - padding: 50px 50px 50px 35px; - - &.wide { - width: 870px; - } - - .info { - float: right; - margin-right: -30px; - margin-left: 0; - } - - .section-title { - color: #fff; - } - } + &.btn-rdn-primary { + background: #A1BB38; + border: none; + color: white; + text-transform: uppercase; - .section-title { - margin: 0; + &:hover { + background: $primary-color } } -} - -.section.country_conventions { - background-image: image-url('backgrounds/bkg_country.png'); - background-position: right 0; - - .section-subtitle { margin-bottom: 20px; } - - ul { - margin-right: -15px; - } - li { - display: block; - position: relative; - float: left; - margin: 0 15px 15px 0; - padding-top: 25px; - width: 120px; - height: 67px; - border: 2px solid #aaa; - font-weight: bold; - font-size: 12px; - @extend .sans-serif; - text-align: center; - color: #999; - @include border-radius(2px); - - span { - position: absolute; - display: block; - left: 0; right: 0; bottom: 0; - padding: 5px; - border-top: 1px solid #ccc; - font-weight: normal; - font-style: italic; - line-height: 12px; - text-transform: uppercase; - } + &.disabled { + @include opacity(.4); + pointer-events: none; + cursor: default; } } -.country_external_links { - font-size: 13px; - color: #ddd; - margin-bottom: -10px; - - li { - @include inline-block(); - border-left: 1px solid #ddd; - padding-left: 8px; - margin: 0 0 10px 5px; - line-height: 14px; - - &:first-child { - border: 0; - margin-left: 0; - padding-left: 0; - } - } - - a { - text-decoration: underline; - } -} - -.country_download_links { - li { - @include inline-block(); - margin-left: 7px; - - a, - span { - @include inline-block(); - border: 2px solid rgba($cGreen, .5); - padding: 0 20px; - font-weight: bold; - font-size: 13px; - line-height: 44px; - text-transform: uppercase; - color: #333; - @include border-radius(25px); - } - - a:hover { - border: 2px solid $cGreen; - } - - span { @include opacity(.4); } - } -} - -.country_blog { - text-align: center; - - .columns { - padding: 0; - min-height: 340px; - } - - .column-title { - position: relative; - top: -40px; - font-weight: bold; - font-size: 13px; - text-transform: uppercase; - color: #555; - } - - .story-title { - margin-bottom: 20px; - font-size: 21px; - @extend .serif; - color: #FFF; - } - - .story-content { - margin-bottom: 30px; - color: #666; - - a { - font-weight: normal; - text-decoration: underline; - color: #666; - } - } - - .column { - display: block; - margin-top: 40px; - min-height: 266px; - - &.first, - &.last { - margin-top: 52px; - - .column-title { - top: -52px; - } - } - - &.first { - text-align: left; - } - - &.last { - text-align: right; - } - - &.no_story { - .frame { - background: #111; - } - - .gradient { - background-image: image-url('backgrounds/bkg_circles_gradient_nostory.png'); - } - } - - .inline { - display: inline; - width: auto; - height: auto; - } - } -} - - -/* =Overview ------------------------------------------------ */ - -.overview_graph { - margin: -106px auto 0; - height: 511px; - - @include background-image(linear-gradient(#464253, #383643)); -} - -.overview_graph__area { - height: 371px; - background: image-url('backgrounds/overview_graph.png'); - @include background-size(cover); - - svg { - position: relative; - z-index: 300; - margin-left: -40px; - } -} - -.tick { - fill: none; - shape-rendering: crispEdges; - stroke-width: 1px; - stroke-dasharray: 8, 4; -} - -.grid_h { - @extend .tick; - stroke: rgba(#fff, .1); -} - -.grid_v { - @extend .tick; - stroke: rgba(#fff, .3); -} - -.area { - @include opacity(.35); -} - -.linedot { - stroke-width: 2px; - stroke: #464253; - fill: #fff; - cursor: pointer; - - &.gain { - stroke: #98BD17; - } -} - -.overview_line { - @extend .tick; - stroke-width: 3px; - stroke: #98BD17; -} - -.minioverview_line { - @extend .tick; - stroke: #444; - - @include opacity(.5); -} - -.overview_graph__tabs { - @extend .clearfix; - - li { - display: table; - float: left; - - &:first-child a { - border-left: 0; - padding-left: 1px; - } - - &.selected, - &:hover { - a { - position: relative; - border-bottom: 0; - color: rgba(#fff, .75); - background: rgba(#000, .2); - } - } - - &.selected a:after { - content: ''; - position: absolute; - top: 0; left: 50%; - margin-left: -3px; - border: 6px solid transparent; - border-top-color: #fff; - } - - &.all_countries a { - background: #95BC3B; - margin-bottom: 0; - - &:hover { - background: darken(#95BC3B, 5%); - border-bottom: 1px solid #615D6C; - } - } - } -} - -.overview_graph__link { - display: table-cell; - height: 99px; - width: 159px; - border-left: 1px solid #615D6C; - border-bottom: 1px solid #615D6C; - font-size: 14px; - font-family: "fira_sans_otmedium"; - vertical-align: middle; - text-align: center; - text-transform: uppercase; - color: rgba(#fff, .75); - - span { - display: block; - font-size: 12px; - line-height: 1.5; - color: #A39FAA; - } -} - -.overview_graph__legend { - float: left; - width: 100%; - border-bottom: 1px solid #615D6C; - margin-top: -1px; - line-height: 40px; - font-size: 13px; - font-family: "fira_sans_otmedium"; - text-align: center; - background: rgba(#000, .2); - color: #fff; - - .info { - float: right; - margin-right: 20px; - } -} - -.overview_graph__years, -.overview_graph__axis, -.countries_list__header { - position: relative; - height: 28px; - border-bottom: 1px solid #CCC; - line-height: 28px; - font-size: 12px; - font-family: "fira_sans_otmedium"; - color: #9D9AA5; - text-transform: uppercase; - text-align: center; - - span { - text-transform: lowercase; - } -} - -.overview_graph__axis { - display: none; - text-transform: none; -} - -.year { - display: block; - position: absolute; - width: 30px; - margin-left: -15px; - text-align: center; -} - -.overview_graph__title { - border-bottom: 1px solid #ccc; - padding: 30px 0; - font-size: 37px; - line-height: 1.1; - font-family: "fira_sans_otregular"; - text-align: center; - color: #463F52; - vertical-align: top; - - sup { - position: relative; - top: -14px; - font-size: 23px; - } -} - -.countries_list ul li { - @extend .clearfix; - border-bottom: 1px solid #ccc; - padding: 18px 0; - - &:hover { - background: rgba(#ddd, .3); - - .bar { - @include opacity(1); - } - - .minioverview_line { - @include opacity(1); - } - - .countries_list__minioverview { - .loss { - color: $cGreen; - } - - .gain { - color: #C443FF; - } - } - } -} - -.countries_list__header__minioverview, -.countries_list__minioverview { - float: right; - width: 110px; - text-align: center; - - &.huge { - width: 330px; - } - - &.expanded { - width: 240px; - } - - &.medium { - width: 150px; - } -} - -.countries_list__header__num, -.countries_list__num, -.countries_list__header__title, -.countries_list__title { - float: left; -} - -.countries_list__header__num, -.countries_list__num { - width: 45px; - padding-left: 10px; - padding-right: 10px; -} - -.countries_list__header__title, -.countries_list__title { - padding-left: 15px; -} - -.countries_list__num, -.countries_list__title { - height: 30px; - padding-top: 10px; - padding-bottom: 10px; - font-size: 29px; - line-height: 30px; - font-family: "fira_sans_otmedium"; - color: #464152; -} - -.countries_list__num { - width: 45px; -} - -.countries_list__title { - border-left: 1px solid #ccc; -} - -.countries_list__minioverview { - height: 50px; - font-size: 29px; - line-height: 50px; - font-family: "fira_sans_otmedium"; - color: #A2A0A9; - - .loss { - color: rgba($cGreen, .5); - } - - .gain { - color: rgba(#C443FF, .5); - } - - .bar { - fill: #C443FF; - - &.extent { - fill: $cGreen; - } - } - - svg { - float: right; - margin: 10px; - } - - .sibling { - float: left; - border-right: 1px solid #ccc; - - svg { - padding-right: 10px; - } - } - - .half { - float: left; - width: 150px; - - &.last { - padding-left: 9px; - border-left: 1px solid #ccc; - margin-left: 10px; - } - } -} - -.countries_list__footer { - background: rgba(#98BD17, .15); - text-align: center; - padding: 30px 0; - - a { - display: inline-block; - border: 2px solid rgba(#464253, .25); - width: 192px; - padding: 0 20px; - font-size: 14px; - line-height: 38px; - font-family: "fira_sans_otmedium"; - text-align: left; - text-transform: uppercase; - color: #464152; - @include border-radius(21px); - - i { - float: right; - display: block; - height: 38px; - width: 10px; - @include country-icons-sprite(overview_footer); - } - - &:hover { - border: 2px solid rgba(#464253, .5); - } - } -} - -.tooltip { - position: absolute; - z-index: 500; - visibility: hidden; - padding: 20px 10px; - font-size: 19px; - font-family: "fira_sans_otmedium"; - text-align: center; - color: #C440FF; - background: #fff; - @include border-radius(5px); - @include box-shadow(0 0 3px #000); - - &:after { - content: ''; - position: absolute; - bottom: -6px; left: 50%; - border-color: #fff rgba(#fff, 0); - border-width: 6px 6px 0 6px; - border-style: solid; - margin-left: -6px; - } - - &.gain_tooltip, - &.tropical_tooltip { - color: #98BD17; - } - - &.subtropical_tooltip { - color: #FFFF73; - } - - &.boreal_tooltip { - color: #FFB973; - } - - &.temperature_tooltip { - color: #73DCFF; - } - - span { - display: block; - font-size: 15px; - line-height: 1.5; - color: rgba(#373343, .5); - } -} - -.chart { - display: none; - position: absolute; - width: 960px; - height: 371px; - color: #fff; - - &.total_loss { - display: block; - } - - &.percent_loss, - &.total_extent { - .disclaimer { - bottom: 40px; - } - } - - &.total_extent { - .legend { - top: 40px; - } - } - - .legend { - position: absolute; - z-index: 400; - top: 10px; left: 20px; - - li { - font-size: 19px; - line-height: 1.5; - font-family: "fira_sans_otregular"; - @include text-shadow(0 0 5px rgba(#000, .8)); - - &:first-child i { - background: #C441FF; - } - - &:last-child i { - background: #9ABF00; - } - - i { - display: inline-block; - position: relative; - top: -2px; - height: 6px; - width: 6px; - vertical-align: middle; - @include border-radius(4px); - } - } - } - - .disclaimer { - position: absolute; - z-index: 400; - left: 20px; bottom: 20px; - margin: 0; - font: normal 11px/1 "fira_sans_otmedium"; - color: #fff; - } -} - -.ball { - fill: #98BD17; - @include opacity(.8); -} - -.label { - font-size: 13px; - font-family: "fira_sans_otmedium"; - text-transform: uppercase; - fill: #fff; -} - -.axis { - font-size: 12px; - font-family: "fira_sans_otmedium"; - fill: #9D9AA5; -} - -.source_window .conventions .credits { - margin-bottom: 30px; -} - -.countries_list { - @extend .clearfix; -} - -.disclaimer { - margin-top: 40px; - font-style: italic; - line-height: 1.4; - @extend .serif; - color: #666; -} -.share_dialog { - $width: 463px; - $height: 170px; - - display: none; - position: fixed; - z-index: 2000; - top: 50%; left: 50%; - margin-top: -$height/2; - margin-left: -$width/2; - height: $height; - width: $width; - padding: 30px; - border: 1px solid #757573; - background: #fff; - @include box-shadow(0 0 7px #666); - @include border-radius(3px); - text-align: center; - - .form { - position: absolute; - bottom: 0; left: 0; - border-top: 1px solid #f1f1f1; - padding: 28px 10px 30px; - width: 498px; - } - - .error_input_label { - display: none; - position: absolute; - right: -92px; bottom: 37px; - width: 190px; - height: 39px - 9px; - padding: 9px 0 0; - color: #fff; - font-size: 15px; - @extend .sans-serif; - text-align: center; - @include country-icons-sprite(error_input_label); - } - - .btn { - width: 120px; - padding: 14px 0; - } - - .input-field { - position: relative; - width: 300px; - margin: 0 30px 0 0; - - &.error input { color: red;} - - .icon.error { - display: none; - width: 21px; - height: 22px; - position:absolute; - right: -7px; - top: 10px; - @include country-icons-sprite(input_error); - } - } - - .holder { - position: absolute; - top: 14px; left: 12px; - font-size: 15px; - @extend .sans-serif; - color: #ddd; - } - - .subtitle { - margin: 0 0 20px 0; - - a { - color: #A1BB27; - text-decoration: underline; - } - } - - .close { - display: block; - position: absolute; - top: 15px; right: 15px; - width: 6px; - height: 6px; - @include country-icons-sprite(infowindow_close); - } - - h1 { - font-size: 35px; - margin: 10px 0 5px; - @extend .serif; - } - - p { - font-size: 15px; - @extend .sans_serif; - color: #999; - - &.help { margin: 0 0 20px; } - } - .mode_menu { - position: absolute; - top: 4px; right: -15px; - width: 120px; - background: #fff; - - li.first a { - @include border-right-radius(3px); - } - - li.last a { - @include border-left-radius(3px); - } - - li.selected a { - background: $cGreen; - color: #fff; - } - - a { - display: block; - float: right; - width: 55px; - margin-left: 2px; - font-weight: bold; - font-size: 11px; - line-height: 34px; - text-transform: uppercase; - background: #eee; - } - } - .input-field { - @include inline-block(); - height:36px; - width: 416px; - - background: url(backgrounds/bkg_form_input.png) no-repeat left 0; - - input[type="text"], input[type="password"], input[type="email"],input[type="date"] { - width:100%; - height:37px; - margin: 0 0 0 7px; - padding: 0 7px 0 5px; - - font-size: 14px; - - background:url(backgrounds/bkg_form_input.png) repeat-x right -36px; - border:none; - outline:none; - } - &.huge { - height: 42px; - background:url(backgrounds/bkg_form_input_big.png) no-repeat left 0; - - input[type="password"], input[type="text"], input[type="email"] { - height:43px; - background:url(backgrounds/bkg_form_input_big.png) repeat-x right -42px; - } - } - } -} -.share_dialog { - height: 210px; - - .input-field { - width: 444px; - margin: 0 30px 0 20px; - } - - .share_buttons { - float: left; - width: 112px; - - a { - display: block; - float: left; - width: 32px; - height: 32px; - margin-right: 5px; - background: image-url('home-icons/ess-icons-2.png') no-repeat; - - &.twitter { background-position: -102px 0; } - &.facebook { background-position: -68px 0; } - - span { display: none; } - } - } -} \ No newline at end of file diff --git a/app/assets/stylesheets/countries/index.scss b/app/assets/stylesheets/countries/index.scss new file mode 100644 index 0000000000..27b024b040 --- /dev/null +++ b/app/assets/stylesheets/countries/index.scss @@ -0,0 +1,97 @@ +.countries_block { + height: 120px; + margin-top: -10px; + background: + image-url('backgrounds/bkg_countries_block_up.png') no-repeat 0 center, + image-url('backgrounds/bkg_countries_block_down.png') repeat-x; + margin-bottom: 40px; + + .header-title { + font-size: 29px; + line-height: 120px; + color: #fff; + + a:hover { + text-decoration: underline; + } + } + span{ + color: darken($cGreen, 5%); + } +} + +.country { + float: left; + margin: 0 0 -1px -1px; + font-size: 15px; + line-height: 18px; + text-align: center; + color: #999; + + a { + position: relative; + display: block; + width: 199px; + height: 230px; + padding: 20px; + border: 1px solid #E5E5E5; + background: #fff; + + &:hover { + z-index: 1000; + margin: -10px; + padding: 30px; + @include box-shadow(0 0 5px rgba(#000, .1)); + + .country_main, + .country_alt { + fill: $cGreen; + } + + .country-content { + position: absolute; + top: 200px; left: 30px; right: 30px; + } + + svg { + top: 30px; + } + } + } + + .country-content { + position: absolute; + top: 190px; left: 20px; + right: 20px; + + span { + display: block; + color: #999; + font-size: 15px; + line-height: 1.1; + } + } + + h3 { + display: block; + margin-bottom: 5px; + font-size: 29px; + line-height: 1.1; + @extend .serif; + color: #666; + + &.small { + font-size: 19px; + } + } + + strong { + font-weight: bold; + } + + svg { + position: absolute; + top: 20px; left: 50%; + margin-left: -75px; + } +} \ No newline at end of file diff --git a/app/assets/stylesheets/countries/overview.scss b/app/assets/stylesheets/countries/overview.scss new file mode 100644 index 0000000000..013e97df17 --- /dev/null +++ b/app/assets/stylesheets/countries/overview.scss @@ -0,0 +1,697 @@ +.overview_graph { + margin: -106px auto 0; + height: 511px; + + @include background-image(linear-gradient(#464253, #383643)); +} + +.overview_graph__area { + height: 371px; + background: image-url('backgrounds/overview_graph.png'); + @include background-size(cover); + + svg { + position: relative; + z-index: 300; + margin-left: -40px; + } +} + +.tick { + fill: none; + shape-rendering: crispEdges; + stroke-width: 1px; + stroke-dasharray: 8, 4; +} + +.grid_h { + @extend .tick; + stroke: rgba(#fff, .1); +} + +.grid_v { + @extend .tick; + stroke: rgba(#fff, .3); +} + +.area { + @include opacity(.35); +} + +.linedot { + stroke-width: 2px; + stroke: #464253; + fill: #fff; + cursor: pointer; + + &.gain { + stroke: #98BD17; + } +} + +.overview_line { + @extend .tick; + stroke-width: 3px; + stroke: #98BD17; +} + +.minioverview_line { + @extend .tick; + stroke: #444; + + @include opacity(.5); +} + +.overview_graph__tabs { + @extend .clearfix; + + li { + display: table; + float: left; + + &:first-child a { + border-left: 0; + padding-left: 1px; + } + + &.selected, + &:hover { + a { + position: relative; + border-bottom: 0; + color: rgba(#fff, .75); + background: rgba(#000, .2); + } + } + + &.selected a:after { + content: ''; + position: absolute; + top: 0; left: 50%; + margin-left: -3px; + border: 6px solid transparent; + border-top-color: #fff; + } + + &.all_countries a { + background: #95BC3B; + margin-bottom: 0; + + &:hover { + background: darken(#95BC3B, 5%); + border-bottom: 1px solid #615D6C; + } + } + } +} + +.overview_graph__link { + display: table-cell; + height: 99px; + width: 159px; + border-left: 1px solid #615D6C; + border-bottom: 1px solid #615D6C; + font-size: 14px; + font-family: "fira_sans_otmedium"; + vertical-align: middle; + text-align: center; + text-transform: uppercase; + color: rgba(#fff, .75); + + span { + display: block; + font-size: 12px; + line-height: 1.5; + color: #A39FAA; + } +} + +.overview_graph__legend { + float: left; + width: 100%; + border-bottom: 1px solid #615D6C; + margin-top: -1px; + line-height: 40px; + font-size: 13px; + font-family: "fira_sans_otmedium"; + text-align: center; + background: rgba(#000, .2); + color: #fff; + + .info { + float: right; + margin-right: 20px; + } +} + +.overview_graph__years, +.overview_graph__axis, +.countries_list__header { + position: relative; + height: 28px; + border-bottom: 1px solid #CCC; + line-height: 28px; + font-size: 12px; + font-family: "fira_sans_otmedium"; + color: #9D9AA5; + text-transform: uppercase; + text-align: center; + + span { + text-transform: lowercase; + } +} + +.overview_graph__axis { + display: none; + text-transform: none; +} + +.year { + display: block; + position: absolute; + width: 30px; + margin-left: -15px; + text-align: center; +} + +.overview_graph__title { + border-bottom: 1px solid #ccc; + padding: 30px 0; + font-size: 37px; + line-height: 1.1; + font-family: "fira_sans_otregular"; + text-align: center; + color: #463F52; + vertical-align: top; + + sup { + position: relative; + top: -14px; + font-size: 23px; + } +} + +.countries_list ul li { + @extend .clearfix; + border-bottom: 1px solid #ccc; + padding: 18px 0; + + &:hover { + background: rgba(#ddd, .3); + + .bar { + @include opacity(1); + } + + .minioverview_line { + @include opacity(1); + } + + .countries_list__minioverview { + .loss { + color: $cGreen; + } + + .gain { + color: #C443FF; + } + } + } +} + +.countries_list__header__minioverview, +.countries_list__minioverview { + float: right; + width: 110px; + text-align: center; + + &.huge { + width: 330px; + } + + &.expanded { + width: 240px; + } + + &.medium { + width: 150px; + } +} + +.countries_list__header__num, +.countries_list__num, +.countries_list__header__title, +.countries_list__title { + float: left; +} + +.countries_list__header__num, +.countries_list__num { + width: 45px; + padding-left: 10px; + padding-right: 10px; +} + +.countries_list__header__title, +.countries_list__title { + padding-left: 15px; +} + +.countries_list__num, +.countries_list__title { + height: 30px; + padding-top: 10px; + padding-bottom: 10px; + font-size: 29px; + line-height: 30px; + font-family: "fira_sans_otmedium"; + color: #464152; +} + +.countries_list__num { + width: 45px; +} + +.countries_list__title { + border-left: 1px solid #ccc; +} + +.countries_list__minioverview { + height: 50px; + font-size: 29px; + line-height: 50px; + font-family: "fira_sans_otmedium"; + color: #A2A0A9; + + .loss { + color: rgba($cGreen, .5); + } + + .gain { + color: rgba(#C443FF, .5); + } + + .bar { + fill: #C443FF; + + &.extent { + fill: $cGreen; + } + } + + svg { + float: right; + margin: 10px; + } + + .sibling { + float: left; + border-right: 1px solid #ccc; + + svg { + padding-right: 10px; + } + } + + .half { + float: left; + width: 150px; + + &.last { + padding-left: 9px; + border-left: 1px solid #ccc; + margin-left: 10px; + } + } +} + +.countries_list__footer { + background: rgba(#98BD17, .15); + text-align: center; + padding: 30px 0; + + a { + display: inline-block; + border: 2px solid rgba(#464253, .25); + width: 192px; + padding: 0 20px; + font-size: 14px; + line-height: 38px; + font-family: "fira_sans_otmedium"; + text-align: left; + text-transform: uppercase; + color: #464152; + @include border-radius(21px); + + i { + float: right; + display: block; + height: 38px; + width: 10px; + @include country-icons-sprite(overview_footer); + } + + &:hover { + border: 2px solid rgba(#464253, .5); + } + } +} + +.tooltip { + position: absolute; + z-index: 500; + visibility: hidden; + padding: 20px 10px; + font-size: 19px; + font-family: "fira_sans_otmedium"; + text-align: center; + color: #C440FF; + background: #fff; + @include border-radius(5px); + @include box-shadow(0 0 3px #000); + + &:after { + content: ''; + position: absolute; + bottom: -6px; left: 50%; + border-color: #fff rgba(#fff, 0); + border-width: 6px 6px 0 6px; + border-style: solid; + margin-left: -6px; + } + + &.gain_tooltip, + &.tropical_tooltip { + color: #98BD17; + } + + &.subtropical_tooltip { + color: #FFFF73; + } + + &.boreal_tooltip { + color: #FFB973; + } + + &.temperature_tooltip { + color: #73DCFF; + } + + span { + display: block; + font-size: 15px; + line-height: 1.5; + color: rgba(#373343, .5); + } +} + +.chart { + display: none; + position: absolute; + width: 960px; + height: 371px; + color: #fff; + + &.total_loss { + display: block; + } + + &.percent_loss, + &.total_extent { + .disclaimer { + bottom: 40px; + } + } + + &.total_extent { + .legend { + top: 40px; + } + } + + .legend { + position: absolute; + z-index: 400; + top: 10px; left: 20px; + + li { + font-size: 19px; + line-height: 1.5; + font-family: "fira_sans_otregular"; + @include text-shadow(0 0 5px rgba(#000, .8)); + + &:first-child i { + background: #C441FF; + } + + &:last-child i { + background: #9ABF00; + } + + i { + display: inline-block; + position: relative; + top: -2px; + height: 6px; + width: 6px; + vertical-align: middle; + @include border-radius(4px); + } + } + } + + .disclaimer { + position: absolute; + z-index: 400; + left: 20px; bottom: 20px; + margin: 0; + font: normal 11px/1 "fira_sans_otmedium"; + color: #fff; + } +} + +.ball { + fill: #98BD17; + @include opacity(.8); +} + +.label { + font-size: 13px; + font-family: "fira_sans_otmedium"; + text-transform: uppercase; + fill: #fff; +} + +.axis { + font-size: 12px; + font-family: "fira_sans_otmedium"; + fill: #9D9AA5; +} + +.source_window .conventions .credits { + margin-bottom: 30px; +} + +.countries_list { + @extend .clearfix; +} + +.disclaimer { + margin-top: 40px; + font-style: italic; + line-height: 1.4; + @extend .serif; + color: #666; +} +.share_dialog { + $width: 463px; + $height: 170px; + + display: none; + position: fixed; + z-index: 2000; + top: 50%; left: 50%; + margin-top: -$height/2; + margin-left: -$width/2; + height: $height; + width: $width; + padding: 30px; + border: 1px solid #757573; + background: #fff; + @include box-shadow(0 0 7px #666); + @include border-radius(3px); + text-align: center; + + .form { + position: absolute; + bottom: 0; left: 0; + border-top: 1px solid #f1f1f1; + padding: 28px 10px 30px; + width: 498px; + } + + .error_input_label { + display: none; + position: absolute; + right: -92px; bottom: 37px; + width: 190px; + height: 39px - 9px; + padding: 9px 0 0; + color: #fff; + font-size: 15px; + @extend .sans-serif; + text-align: center; + @include country-icons-sprite(error_input_label); + } + + .btn { + width: 120px; + padding: 14px 0; + } + + .input-field { + position: relative; + width: 300px; + margin: 0 30px 0 0; + + &.error input { color: red;} + + .icon.error { + display: none; + width: 21px; + height: 22px; + position:absolute; + right: -7px; + top: 10px; + @include country-icons-sprite(input_error); + } + } + + .holder { + position: absolute; + top: 14px; left: 12px; + font-size: 15px; + @extend .sans-serif; + color: #ddd; + } + + .subtitle { + margin: 0 0 20px 0; + + a { + color: #A1BB27; + text-decoration: underline; + } + } + + .close { + display: block; + position: absolute; + top: 15px; right: 15px; + width: 6px; + height: 6px; + @include country-icons-sprite(infowindow_close); + } + + h1 { + font-size: 35px; + margin: 10px 0 5px; + @extend .serif; + } + + p { + font-size: 15px; + @extend .sans_serif; + color: #999; + + &.help { margin: 0 0 20px; } + } + .mode_menu { + position: absolute; + top: 4px; right: -15px; + width: 120px; + background: #fff; + + li.first a { + @include border-right-radius(3px); + } + + li.last a { + @include border-left-radius(3px); + } + + li.selected a { + background: $cGreen; + color: #fff; + } + + a { + display: block; + float: right; + width: 55px; + margin-left: 2px; + font-weight: bold; + font-size: 11px; + line-height: 34px; + text-transform: uppercase; + background: #eee; + } + } + .input-field { + @include inline-block(); + height:36px; + width: 416px; + + background: url(backgrounds/bkg_form_input.png) no-repeat left 0; + + input[type="text"], input[type="password"], input[type="email"],input[type="date"] { + width:100%; + height:37px; + margin: 0 0 0 7px; + padding: 0 7px 0 5px; + + font-size: 14px; + + background:url(backgrounds/bkg_form_input.png) repeat-x right -36px; + border:none; + outline:none; + } + &.huge { + height: 42px; + background:url(backgrounds/bkg_form_input_big.png) no-repeat left 0; + + input[type="password"], input[type="text"], input[type="email"] { + height:43px; + background:url(backgrounds/bkg_form_input_big.png) repeat-x right -42px; + } + } + } +} +.share_dialog { + height: 210px; + + .input-field { + width: 444px; + margin: 0 30px 0 20px; + } + + .share_buttons { + float: left; + width: 112px; + + a { + display: block; + float: left; + width: 32px; + height: 32px; + margin-right: 5px; + background: image-url('home-icons/ess-icons-2.png') no-repeat; + + &.twitter { background-position: -102px 0; } + &.facebook { background-position: -68px 0; } + + span { display: none; } + } + } +} \ No newline at end of file diff --git a/app/assets/stylesheets/countries/show.scss b/app/assets/stylesheets/countries/show.scss new file mode 100644 index 0000000000..5c0cfb6a71 --- /dev/null +++ b/app/assets/stylesheets/countries/show.scss @@ -0,0 +1,1007 @@ +.country-show { + font-family: $font-regular; + color: $default-color; + margin-top: -128px; + + .share { + text-align: right; + margin-bottom: 12px; + display: block; + } + + .share_control { + position: relative; + width: auto; + height: 32px; + left: auto; + right: auto; + top: auto; + display: inline-block; + background: none; + border: 2px solid #C4C3C7; + padding: 6px 12px 0 12px; + text-transform: uppercase; + border-radius: 20px; + font-size: 14px; + color: #464152; + @include box-sizing(border-box); + + .country-icons-share { + height: 16px; + width: 16px; + display: inline-block; + vertical-align: bottom; + margin-left: 5px; + } + + &:hover { + border-color: #BAB8BE; + } + } + + .country-header { + width: 100%; + + .country-header-inner { + $wall-background: "backgrounds/wall_background_alpha.png"; + background: #464253; + background-image: image-url($wall-background); /* fallback */ + } + + .country-title { + padding: 20px; + + h1 { + color: white; + font-size: 47px; + max-width: 545px; + margin-right: 20px; + display: inline-block; + font-family: $font-regular; + } + + .country-selector { + display: inline-block; + vertical-align: top; + margin-top: 2px; + border: 2px solid #73707D; + border-radius: 40px; + height: 39px; + width: 310px; + position: relative; + } + + select { + border: none; + font-size: 13px; + text-transform: uppercase; + background: transparent; + color: #BEBCC2; + -webkit-appearance: none; + padding: 10px 15px; + width: 100%; + + option { + color: #666; + } + + &:focus { + outline: 0; + } + } + + .selector-arrow { + border-color: #73707D transparent; + border-style: solid; + border-width: 6px 5px 0 5px; + width: 0; + height: 0; + top: 50%; + right: 20px; + margin-top: -3px; + position: absolute; + pointer-events: none; + } + + .selector-remove { + padding: 5px; + color: #73707D; + font-size: 13px; + top: 9px; + right: 35px; + position: absolute; + cursor: pointer; + display: none; + &:hover { + color: lighten(#73707D, 10); + } + } + } // country-title + + .country-details { + background-color:rgba(0, 0, 0, 0.2); + height: 263px; + + .country-preview { + width: 735px; + height: 100%; + float: left; + @include box-sizing(border-box); + + h4 { + text-transform: uppercase; + color: white; + line-height: 18px; + font-size: 14px; + } + + .map, + .tree-numbers, + .loss-gain-graph { + float: left; + height: 100%; + @include box-sizing(border-box); + } + + .tree-numbers, + .loss-gain-graph { + padding: 30px 10px; + } + + .map { + width: 40%; + height: 100%; + background: transparent; + cursor: default; + + &.loaded { + background: #464253; + } + } + + .tree-numbers { + width: 25%; + padding-left: 20px; + + h4 { + width: 100px; + } + + .amount, + .unit { + margin-top: 6px; + color: $primary-color; + display: inline-block; + } + + .amount { + font-size: 47px; + font-weight: bold; + } + + .total-area { + margin-top: 18px; + } + + .total-area .amount { + font-size: 29px; + } + } + + .loss-gain-graph { + width: 35%; + position: relative; + + .coming-soon { + display: none; + position: absolute; + z-index: 10; + top: 70px; + left: 10px; + padding: 30px 0; + margin-right: 10px; + font-size: 17px; + text-transform: uppercase; + text-align: center; + color: #999; + background: $default-color; + + span { + width: 90%; + display: inline-block; + } + } + + &.ghost { + @include opacity(.5); + cursor: default; + } + + h4 { + margin-bottom: 20px; + } + + .bar { + shape-rendering: "crispEdges"; + } + + .graph-amount, + .graph-date { + text-align: center; + display: block; + max-width: 205px; + } + + .graph-amount { + font-size: 30px; + color: $primary-color; + margin-top: 10px; + } + + .graph-date { + color: white; + margin-top: 2px; + font-size: 13px; + } + + .gain-tooltip { + position: absolute; + padding: 10px; + width: 150px; + background: white; + text-align: center; + border-radius: 5px; + font-size: 22px; + color: $primary-color; + + span { + font-size: 13px; + color: #524F5C; + margin-top: 2px; + display: block; + } + + &:before { + content:''; + display:block; + width:0; + height:0; + position:absolute; + + border-left: 8px solid transparent; + border-right: 8px solid transparent; + border-top: 8px solid white; + + right: 0; + left: 0; + margin: auto; + bottom: -8px; + } + } + } // loss-gain-graph + } + + .country-sidenav { + float: right; + width: 215px; + height: 100%; + background: transparent; + border-left: 2px solid #464254; + + > ul { + padding: 10px 0; + width: 100%; + + > li { + width: 100%; + position: relative; + + a { + color: #BEBCC2; + display: block; + height: 53px; + width: 100%; + padding: 20px 25px; + @include box-sizing(border-box); + font-family: $font-medium; + text-transform: uppercase; + font-size: 14px; + cursor: pointer; + } + + a .sidenav-icon { + display: inline-block; + position: absolute; + top: 17px; right: 20px; + height: 18px; + width: 18px; + } + + &:hover { + background: #464254; + } + } + } + } + + } // country-details + } // country-header + + .country-indepth { + clear: both; + border: 1px solid #CCCCCC; + border-bottom: 0; + height: 185px; + width: 100%; + padding: 35px 20px; + font-family: $font-medium; + @include box-sizing(border-box); + background-image: image-url('backgrounds/country-indepth.png'); + background-repeat: no-repeat; + background-position: 96% 50%; + + .country-indepth__title { + font-size: 29px; + } + + .country-indepth__body { + text-transform: uppercase; + font-size: 14px; + font-weight: 700; + margin-top: 20px; + } + + .country-indepth__links { + margin-top: 25px; + color: #bbb; + font-family: $font-light; + font-size: 13px; + + a { + font-size: 14px; + display: inline-block; + vertical-align: top; + color: $primary-color; + font-family: $font-medium; + } + } + + } // country-indepth + + .country-nav-container { + width: 100%; + position: relative; + height: 49px; + + .country-nav { + width: 100%; + border-bottom: 1px solid #CCCCCC; + height: 49px; + position: absolute; + top: 0; + z-index: 100; + } + + .country-nav.fixed { + background: #F2F2F3; + .country-nav-items li a { + border-top: none; + } + } + + .country-nav-items { + width: 100%; + text-align: center; + + > li { + float: left; + margin-right: -1px; + height: 50px; + width: 135px; + @include box-sizing(border-box); + + &:first-child { + width: 156px; + } + + a { + border: 1px solid #ccc; + background: #F2F2F3; + width: 100%; + height: 100%; + display: inline-block; + padding: 11px 15px; + color: $default-color; + font-size: 14px; + text-transform: uppercase; + cursor: pointer; + font-family: $font-medium; + @include box-sizing(border-box); + + &.active { + background: white; + border-bottom: 1px solid white; + color: #999999; + } + } + + &:hover a { + color: $primary-color; + background: #F9F9F9; + + &.active { + background: white; + } + } + } + } // country-nav + } + + .highlight { + color: $primary-color; + } + + .country-section { + border-bottom: 1px solid #E5E5E5; + width: 100%; + padding: 45px 0; + + .section-info { + margin-bottom: 12px; + font-size: 14px; + color: $default-color; + font-family: $font-medium; + text-transform: uppercase; + letter-spacing: -0.2px; + + .info { + margin-right: 6px; + } + } + + .section-content { + padding-left: 30px; + @extend .clearfix; + + .left-col, + .right-col { + float: left; + + &.wide { + width: 100%; + } + } + + .left-col { + width: 40%; + } + + .right-col { + width: 55%; + padding-left: 5%; + } + + .section-title { + font-size: 29px; + line-height: 36px; + font-family: $font-medium; + color: $default-color; + } + } + + .country-alt { + display: inline-block; + width: 80%; + clear: both; + padding-top: 20px; + color: #666; + line-height: 1.4; + font-size: 14px; + } + } + + .country-alerts, + .country-production, + .country-tenure, + .country-employment, + .country-forests-type { + .coming-soon { + width: 100%; + font-size: 16px; + text-transform: uppercase; + text-align: center; + padding: 40px 0; + background: #f2f2f2; + color: #666; + } + } + + .country-tenure, + .country-forests-type { + .coming-soon { + display: none; + } + } + + .country-alerts { + #formaAlertsContent, + #comingSoonContent { + display: none; + } + + .forma_dropdown-menu { + display: none; + } + + .forma-graph path { + stroke: $primary-color; + stroke-width: 4px; + fill: none; + } + + .forma-graph { + position: relative; + } + + .forma-marker { + stroke: white; + stroke-width: 2px; + fill: $primary-color; + } + + .graph-tooltip { + position: absolute; + width: 150px; + height: 78px; + background: $primary-color; + @include box-sizing(border-box); + padding: 12px 10px; + border-radius: 5px; + text-align: center; + z-index: 10; + pointer-events: none; + + .graph-amount { + font-size: 26px; + color: white; + margin-bottom: 4px; + display: block; + } + + .graph-date { + font-size: 12px; + color: #60644D; + .date { + margin-top: 2px; + display: block; + font-size: 14px; + } + } + + @include box-shadow(#ddd 2px 2px 10px); + + &:before { + content:''; + display:block; + width:0; + height:0; + position:absolute; + + border-top: 8px solid transparent; + border-bottom: 8px solid transparent; + border-left: 8px solid $primary-color; + + right: -8px; + margin: auto; + top: 0; + bottom: 0; + } + } + + .forma-alerts-legend { + margin: 5px 0 25px 0; + + span { + color: #666666; + font-size: 13px; + font-family: $font-light; + display: block; + } + + .legend-title { + color: $primary-color; + font-size: 14px; + font-family: $font-medium; + margin-bottom: 3px; + } + } + + .btn { + border-radius: 20px; + border: none; + padding: 10px 20px 8px; + background: $primary-color; + font-family: $font-regular; + font-size: 13px; + } + } + + .country-forests-type { + .forests-type-graph { + margin-top: -25px; + } + + .forest-type-legends { + font-size: 14px; + display: none; + margin-top: 10px; + + .legends-title { + color: $primary-color; + margin-bottom: 10px; + display: block; + } + + .legends-list { + li { + display: block; + margin-bottom: 5px; + color: #777; + font-family: $font-light; + vertical-align: top; + + span { + height: 14px; + width: 14px; + border-radius: 100px; + display: inline-block; + margin-right: 4px; + } + + .regenerated { + background: #819515; + } + + .primary { + background: #A1BA42; + } + + .planted { + background: #DDDDDD; + } + } + } + } + } + + .country-tenure { + .line-graph { + font-weight: bold; + text-transform: uppercase; + + circle { + stroke-width: 2px; + stroke: #f5f5f5; + } + + text { + @extend .sans-serif; + font-size: 12px; + } + + .one { + fill: #75B22E; + } + + .two { + fill: #AAC700; + } + + .three { + fill: #AC0; + } + + .four { + fill: #FFD24D; + } + } + } // country-tenure + + .country-legislation { + .national-policy-links { + margin: 5px 0 4px 0; + font-size: 14px; + text-decoration: underline; + } + } // country-legislation + + .country-carbon-stocks { + .carbon-text, + .emissions-text { + float: left; + padding: 50px 0; + @include box-sizing(border-box); + + .section-info { + margin-bottom: 25px; + } + + &.wide { + width: 100%; + } + } + + .carbon-text { + width: 44%; + margin-right: 4%; + } + + .emissions-text { + width: 52%; + background: #444050; + text-align: right; + padding-right: 20px; + + .section-info { + color: #908E97; + a { + margin-right: 0; + margin-left: 5px; + } + } + + .section-content h3 { + color: white; + padding-right: 28px; + } + } + } // country-carbon-stocks + + .country-employment { + .section-content .left-col { + width: 460px; + } + + .section-content .left-col .section-title { + @extend .clearfix; + margin-bottom: 10px; + font-size: 47px; + display: inline-block; + + div, + span { + margin-top: 8px; + float: left; + } + + span { + width: 250px; + margin: 4px 0 0 10px; + font-size: 11px; + line-height: 1.2; + @extend .sans-serif; + text-transform: uppercase; + color: #444; + } + } + + .man-list { + color: #919191; + } + } // country-employment + + .country-conventions { + ul { + margin-right: -15px; + } + + li { + display: block; + position: relative; + float: left; + margin: 0 10px 10px 0; + padding-top: 25px; + width: 120px; + height: 67px; + border: 2px solid #CCCCCC; + font-weight: bold; + font-size: 12px; + @extend .sans-serif; + text-align: center; + color: #AAAAAA; + @include border-radius(2px); + + span { + position: absolute; + display: block; + left: 0; right: 0; bottom: 0; + padding: 5px; + border-top: 1px solid #E2E2E2; + font-weight: normal; + font-style: italic; + line-height: 12px; + font-size: 11px; + text-transform: uppercase; + } + } + } // country-conventions + + .country-external-links { + color: #ddd; + font-size: 14px; + + .section-content { + padding-left: 0; + padding-bottom: 10px; + } + + li { + @include inline-block(); + border-left: 1px solid #ddd; + padding-left: 8px; + margin: 0 0 10px 5px; + line-height: 14px; + + &:first-child { + border: 0; + margin-left: 0; + padding-left: 0; + } + } + + a { + text-decoration: underline; + } + } // external-links + + .country-download-links { + background-color: #F8FAF0; + text-align: center; + padding: 50px 0; + border-bottom-color: #141414; + + a { + text-transform: uppercase; + padding: 14px 30px 12px 30px; + font-size: 13px; + color: #393939; + font-family: $font-medium; + border-color: #E0E9C1; + margin-top: 0; + + &:first-child { + margin-right: 10px; + } + + &:hover { + border: 2px solid #ACCD00; + } + } + } // download-links + + .country-blog { + text-align: center; + background: #141414; + color: white; + + .section-content { + padding: 0; + + .section-title { + color: #A0B941; + font-family: $font-regular; + font-size: 36px; + margin-bottom: 25px; + } + } + + .columns { + padding: 0; + min-height: 340px; + } + + .column-title { + position: relative; + font-size: 14px; + text-transform: uppercase; + color: #444; + font-family: $font-medium; + margin-bottom: 20px; + display: inline-block; + } + + .story-title { + margin-bottom: 18px; + font-size: 27px; + color: #FFF; + } + + .story-content { + margin-bottom: 30px; + color: #727272; + line-height: 1.3; + font-size: 14px; + + a { + text-decoration: underline; + color: #666; + } + } + + .column { + display: block; + margin-top: 40px; + min-height: 266px; + + &.first, + &.last, + &.round { + width: 33%; + margin: 0; + margin-top: 10px; + padding: 0; + } + + &.round { + a { + margin: auto; + } + + .circle-wrapper { + position: relative; + width: 266px; + height: 100%; + margin: auto; + margin-bottom: 20px; + } + + .title strong, + .title span { + font-family: $font-medium; + font-weight: normal; + font-size: 21; + } + + .title span { + font-size: 14px; + } + } + + &.first { + text-align: left; + } + + &.last { + text-align: right; + } + + &.no_story { + .gradient { + background-image: image-url('backgrounds/bkg_circles_gradient_nostory.png'); + } + } + + .inline { + display: inline; + width: auto; + height: auto; + } + + .frame { + width: 266px; + background: #5E5E5E; + } + + .see-more { + text-transform: uppercase; + font-size: 14px; + color: #797979; + font-family: $font-medium; + } + } + } // country-blog + + .country-comments { + background-color: #F5F5F5; + } // country-comments +} \ No newline at end of file diff --git a/app/assets/stylesheets/home.scss b/app/assets/stylesheets/home.scss index 5614b4a16e..a6d653b2e0 100644 --- a/app/assets/stylesheets/home.scss +++ b/app/assets/stylesheets/home.scss @@ -1570,7 +1570,7 @@ body { font-size: 11px; font-weight: bold; position: relative; - width: 159px; + width: 156px; .source { top: 1px; right: 7px; diff --git a/app/controllers/countries_controller.rb b/app/controllers/countries_controller.rb index 30fb1d6727..31738f8b7c 100644 --- a/app/controllers/countries_controller.rb +++ b/app/controllers/countries_controller.rb @@ -1,19 +1,24 @@ class CountriesController < ApplicationController before_filter :load_countries, :only => [:index] + include ActionView::Helpers::NumberHelper - # GET /country/:id def show - country = Api::Country.find_by_iso(params[:id])['countries'][0] + @country = Api::Country.find_by_iso(params[:id])['countries'][0] + not_found unless @country.present? - not_found unless country.present? + if @country['gva'].present? && @country['gva'] > 0 + gva_precision = (@country['gva_percent'] < 0.1) ? 2 : 1 + @country['gva_percent'] = number_to_percentage(@country['gva_percent'], precision: gva_precision) + end + + @employees = @country['employment'] + @conventions = %w(cbd unfccc kyoto unccd itta cites ramsar world_heritage nlbi ilo) - blog_story = Api::Blog.find_by_country(country) + blog_story = Api::Blog.find_by_country(@country) @blog_story = blog_story.present? ? blog_story : nil - response = Typhoeus.get("https://wri-01.cartodb.com/api/v2/sql?q=SELECT%20*%20FROM%20mongabaydb%20WHERE%20position('#{I18n.transliterate(country['name']).downcase.gsub(" ", "_")}'%20in%20keywords)%20%3C%3E%200", headers: { "Accept" => "application/json" }) + response = Typhoeus.get("https://wri-01.cartodb.com/api/v2/sql?q=SELECT%20*%20FROM%20mongabaydb%20WHERE%20position('#{I18n.transliterate(@country['name']).downcase.gsub(" ", "_")}'%20in%20keywords)%20%3C%3E%200", headers: { "Accept" => "application/json" }) @mongabay_story = response.success? ? JSON.parse(response.body)['rows'][0] : nil - - @country = country end private diff --git a/app/helpers/countries_helper.rb b/app/helpers/countries_helper.rb index 42964a882b..179fddee00 100644 --- a/app/helpers/countries_helper.rb +++ b/app/helpers/countries_helper.rb @@ -4,16 +4,25 @@ def map_coords "#{map_path}/5/#{@country['lat'].round(2)}/#{@country['lng'].round(2)}/#{@country['iso']}" end - def extent_to_human(extent) + def extent_to_human(extent, options = {}) + options = {format: '%n %u', shortUnit: false}.merge(options) + if extent < 1000 - result = "#{number_with_delimiter(extent.round(1))}" + number = "#{number_with_delimiter(extent.round(0))}" + unit = options[:shortUnit] ? "Ha" : '' elsif extent >= 1000 && extent < 1000000 - result = "#{number_with_delimiter((extent/1000).round(1))} thousand" + number = "#{number_with_delimiter((extent/1000).round(1))}" + unit = options[:shortUnit] ? "KHa" : 'thousand' else - result = "#{number_with_delimiter((extent/1000000).round(1))} million" + number = "#{number_with_delimiter((extent/1000000).round(1))}" + unit = options[:shortUnit] ? "MHa" : 'million' end - return result + string = options[:format] + string.gsub!('%n', number) + string.gsub!('%u', unit) + + return string end def gva_to_human(gva) diff --git a/app/views/countries/_header.html.erb b/app/views/countries/_header.html.erb new file mode 100644 index 0000000000..0da1f3a605 --- /dev/null +++ b/app/views/countries/_header.html.erb @@ -0,0 +1,66 @@ +
    +
    + + +
    +
    +

    <%= @country['name'] %>

    +
    + +
    +
    +
    +
    + +
    +
    +
    +
    +
    +

    Tree cover

    + <%= extent_to_human(@country['extent'], format: '%n') %> + <%= extent_to_human(@country['extent'], format: '%u', shortUnit: true) %> +
    +
    +

    Percent Tree Cover

    + 30 + % +
    +
    +
    +

    LOSS AND GAIN (2001 - 2012)

    +
    +
    +
    +
    Not yet available for this country
    +
    +
    + +
    +
    + +
    +
    \ No newline at end of file diff --git a/app/views/countries/show.html.erb b/app/views/countries/show.html.erb index c04cf70f32..662a3bd739 100644 --- a/app/views/countries/show.html.erb +++ b/app/views/countries/show.html.erb @@ -10,257 +10,400 @@ <% end %> -<% content_for :title do %> -

    <%= @country['name'] %>

    +<% content_for :css do %> + <% end %> -<%= render 'shared/circles' %> +
    -<% if @country['indepth'].present? %> -
    -
    -

    Global Forest Watch does in-depth work in this country. Find more information <%= link_to 'here', @country['indepth'] %>.

    + + -
    -<% end %> -
    - -
    - -
    -
    -
    - - <% if @country['extent'] > 0 %>

    <%= "#{@country['name'].titleize} has #{extent_to_human(@country['extent'])} hectares of tree cover.".html_safe %>

    <% end %> - -
    0 %>'> -

    The type of forest in this country is...

    -
    +
    + <% end %> + + +
    + - - <% if @country['country_alt'].present? %> -

    <%= @country['country_alt'].html_safe %>

    - <% end %>
    -
    -<% if @country['gva'].present? && @country['gva'] > 0 %> -
    -
    -
    0 && @country['employment'] < 25 %>'> -

    Economic value of <%= @country['name'].titleize %>'s forestry sector

    + +
    +
    + + +
    +
    Not available for this country
    +
    + +
    +
    +

    There were FORMA alerts in

    + +
    + Forest Clearing Alerts + Humid Tropics +
    - <% gva_percent = (@country['gva_percent'] < 0.1) ? number_to_percentage(@country['gva_percent'], precision: 2) : number_to_percentage(@country['gva_percent'], precision: 1) %> +
    + +
    + Download data +
    -

    <%= "The forestry sector contributed USD #{gva_to_human(@country['gva'])} to the economy in 2006, which is approximately #{gva_percent} of the GDP.".html_safe %>

    +
    +
    +
    +
    +
    - <% employees = @country['employment'] %> + +
    +
    + + +
    +
    No data available
    +
    +
    + Forest Type: +
      +
    • Regenerated
    • +
    • Primary
    • +
    • Planted
    • +
    +
    +
    +
    +
    +
    + + <% if @country['country_alt'].present? %> +
    +

    <%= @country['country_alt'].html_safe %>

    +
    + <% end %> +
    +
    +
    - <% if employees.present? && employees > 0 %> -
      '> -
    • -

      Employment

      + +
      +
      + + +
      + <% if @country['gva'].present? && @country['gva'] > 0 %> +

      + The forestry sector contributed USD <%= gva_to_human(@country['gva'])%> to the economy in 2006, which is appoximately <%= @country['gva_percent'] %> of the GDP. +

      + <% else %> +
      No data available
      + <% end %> +
      +
      +
      - <% if employees < 1000 %> -

      <%= employees %>
      thousand people are directly employed by the forestry sector, according to 2006 FAO data

      + +
      +
      + + +
      + <% if @employees.present? && @employees > 0 %> +
      + <% if @employees < 1000 %> +

      <%= @employees %>
      thousand people are directly employed by the forestry sector, according to 2006 FAO data.

      <% else %> -

      <%= (employees/1000.00).round(2) %>
      million people are directly employed by the forestry sector, according to 2006 FAO data

      +

      <%= (@employees/1000.00).round(2) %>
      million people are directly employed by the forestry sector, according to 2006 FAO data.

      <% end %> - <% employees = employees < 100 ? employees : 100 %> -
      - <% employees.times do |i| %> - <%= image_tag 'countries/man.png' %> - <% end %> - - <%= '...' if employees == 100 %> -
      -
    • -
    - <% end %> -
    -
    -<% end %> +
      '> +
    • + <% @employees = @employees < 100 ? @employees : 100 %> +
      + <% @employees.times do |i| %> + <%= image_tag 'countries/man.png' %> + <% end %> + + <%= '...' if @employees == 100 %> +
      +
    • +
    +
    + <% else %> +
    No data available
    + <% end %> + + + + + +
    +
    + + +
    +
    No data available
    +
    +
    +
    +
    -
    -
    -

    Forest tenure

    -
    -
    + +
    +
    + + +
    + <% if @country['national_policy_link'].present? %> + + <% end %> -
    -

    Forest policy and legislation

    -
      - <% if @country['national_policy_link'].present? %> -
    • <%= link_to @country['national_policy_title'].present? ? @country['national_policy_title'] : 'National Forest Policy', @country['national_policy_link'] %>
    • - <% end %> -
    + Are we missing a link? +
    +
    +
    - Are we missing a link? - -
    + +
    +
    -
    -
    -
      + <% if @country['carbon_stocks'].present? && @country['carbon_stocks'] != 0 %> -
    • '> -

      Carbon stocks

      -

      This country has <%= number_with_delimiter(@country['carbon_stocks']) %> million
      metric tons of carbon stocks
      in living forest biomass.

      -
    • +
      + + +
      +

      This country has <%= number_with_delimiter(@country['carbon_stocks']) %> million metric tons of carbon stocks in living forest biomass.

      +
      +
      <% end %> + <% if @country['emissions'].present? && @country['emissions'] != 0 %> -
    • last'> -

      GHG emissions

      +
      - <% precission = (@country['emissions'].abs.to_i < 0.1) ? 2 : 1 %> + + <% precission = (@country['emissions'].abs.to_i < 0.1) ? 2 : 1 %> + +
      <% if @country['emissions'] > 0 %> -

      According to FAO data, <%= number_to_percentage(@country['emissions'], precision: precission) %> of GHG emissions in this country came from land-use change and forestry in 2011.

      +

      According to FAO data, <%= number_to_percentage(@country['emissions'], precision: precission) %> of GHG emissions in this country came from land-use change and forestry in 2011.

      <% else %> -

      According to FAO data, land-use change and forestry sequestered <%= number_to_percentage(@country['emissions'].abs, precision: precission) %> of this country’s GHG emissions in 2011.

      +

      According to FAO data, land-use change and forestry sequestered <%= number_to_percentage(@country['emissions'].abs, precision: precission) %> of this country’s GHG emissions in 2011.

      <% end %> -
    • +
    +
    <% end %> - - -
    -
    -
    -

    Conventions

    - - <% conventions = ['cbd', 'unfccc', 'kyoto', 'unccd', 'itta', 'cites', 'ramsar', 'world_heritage', 'nlbi', 'ilo'] %> - -
      - <% conventions.each do |convention| %> - <% if @country["convention_#{convention}"].present? %> -
    • <%= t('.conventions.'+convention+'_title_html') %> - <%= @country["convention_#{convention}"] %> -
    • - <% end %> - <% end %> -
    -
    -
    + + -<% if @country['ministry_link'].present? || @country['external_links'].present? %> - -<% end %> + <% end %> - - -
    -
    -

    <%= t('.country_blog.title', :country => @country['name'].titleize) %>

    - -
      -
    • - <% unless @blog_story.nil? %> - Blog stories -

      <%= truncate(@blog_story['title'], :length => 35) %>

      -

      <%= truncate(@blog_story['description'].gsub('[…]', ''), :length => 300) %> <%= link_to('more', @blog_story['link'], :target => '_blank') %>

      -

      Read more blog stories '>here

      - <% end %> -
    • - -
    • '> - User stories - <% if @country['story'].present? %> - - " alt="<%= @country['story']['title'] %>" /> -
      -
      -
      -
      - <%= @country['story']['title'] %> - <%= t('.home_stories.featured.read_more') %> -
      -
      + +
    • - -
    • - <% if @mongabay_story.present? %> - Mongabay stories -

      <%= truncate(@mongabay_story['title'], :length => 35) %>

      -

      <%= truncate(@mongabay_story['description'], :length => 300) %> <%= link_to('more', @mongabay_story['loc'], :target => '_blank') %>

      -

      See more on the <%= link_to 'map', "#{map_path}/3/15.00/27.00/ALL/586" %>

      + <%= link_to 'Download relevant data sets','#', class: 'btn-rdn disabled' %> <% end %> -
    • -
    -
    -
    - -
    -
    -
    - - +
    + +
    + + +
    +
    +
    +

    <%= t('.country_blog.title', :country => @country['name'].titleize) %>

    + + + +
    +
    +
    + + +
    +
    +
    + + +
    +
    + +
    + <%= render 'shared/countries' %> + <%= render 'shared/sources' %>
    - -
    - <%= render 'shared/countries' %> - <%= render 'shared/sources' %> -
    + \ No newline at end of file diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb index fd02f847b4..f60ebb5e9b 100644 --- a/app/views/layouts/application.html.erb +++ b/app/views/layouts/application.html.erb @@ -22,6 +22,7 @@ <%= favicon_link_tag 'favicon.ico' %> <%= stylesheet_link_tag "application", :media => "all" %> + <%= yield :css %> <%= controller_stylesheet_link_tag %> <%= javascript_include_tag "modernizr-2.6.2.min" %> <%= csrf_meta_tags %> diff --git a/app/views/shared/_js_templates.html.erb b/app/views/shared/_js_templates.html.erb index da4a6376f4..77cd7a4f1f 100644 --- a/app/views/shared/_js_templates.html.erb +++ b/app/views/shared/_js_templates.html.erb @@ -634,11 +634,9 @@ + <%= javascript_include_tag 'application' %> <%= controller_javascript_include_tag %> diff --git a/config/application.rb b/config/application.rb index b43f2b31c9..452057c0a3 100644 --- a/config/application.rb +++ b/config/application.rb @@ -30,5 +30,7 @@ class Application < Rails::Application I18n.enforce_available_locales = false config.assets.paths << File.join(Rails.root, 'app', 'assets', 'templates') config.autoload_paths += %W(#{config.root}/lib) + + config.generators.assets = false end end diff --git a/index.html b/index.html deleted file mode 100644 index 4a6b635d6f..0000000000 --- a/index.html +++ /dev/null @@ -1,554 +0,0 @@ - - - - - - - - - Commodities Analyzer - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    -
    - - -
    -
    - -
    -
    -
    - -
    -
    -
    -
    -
    - - - - Subscribe to Monthly Alerts - -
    - - - - - - -
    -
    -
    - - - -
    -
    - -
    -
    -
    -
    -
    -
    - -
    -
    -
    - -
    -
    -
    -
    -
    -
    -
    -
    -
    - -
    -
    -
    -
    -
    -
    -
    - -
    -
    -
    -
    -
    -
    -
    -
    - -
    -
    -
    -
    -
    -
    -
    - -
    -
    -
    -
    -
    - -
    - -
    -
    -
    -
    -
    -
    -
    - -
    -
    -
    -
    -
    -
    - -
    -
    -
    -
    -
    -
    -
    - -
    -
    -
    -
    -
    - -
    - -
    -
    - -
    -
    -
    -
    -
    -
    -
    - -
    -
    -
    -
    -
    -
    -
    -
    - -
    -
    -
    -
    -
    -
    -
    - -
    -
    -
    -
    -
    -
    -
    -
    - -
    -
    -
    -
    -
    -
    -
    - -
    -
    -
    -
    -
    -
    -
    -
    - -
    -
    -
    -
    -
    -
    -
    - -
    -
    -
    -
    -
    -
    -
    -
    - - -
    -
    -
    - -
    -
    -
    -
    -
    -
    - A partnership convened by the World Resources Institute -
    -
    - -
    - - -
    - -
    - -
    - -
    -
    -
    - -
    -
    Would you like to...
    -
    -
    -

    Welcome to GFW-Commodities, an online platform that enables companies to analyze the impact of key commodities on forests, using the latest and most powerful data available. For more information, see our About page.

    -

    GFW-Commodities is currently under development and open for “beta” testing. Data and related information on the “beta site” should be considered preliminary.

    -

    Users should not use this information to make decisions about forest management. Additionally, users should not publish or share this information publicly, including media stories and using the data for new research.

    -
    Please enter your password to access the site.
    -
    - - -
    -
    -

    Questions and Notifications:

    -

    To request a password, join our mailing list and be notified of the public launch, or for questions about the platform, please contact GFWbusiness@wri.org. Please provide your email address, name, and company or organization.

    -
    -
    -
    -
    -
    -
    -
    -
    -
    - -
    -
    - -
    -
    - -
    -
    - -
    - - - - - - - - - diff --git a/spec/javascripts/foo_spec.js b/spec/javascripts/foo_spec.js deleted file mode 100644 index cf1cd33764..0000000000 --- a/spec/javascripts/foo_spec.js +++ /dev/null @@ -1,5 +0,0 @@ -describe('Foo', function() { - it("does something", function() { - expect(1 + 1).toBe(2); - }); -}); \ No newline at end of file diff --git a/spec/javascripts/support/jasmine.yml b/spec/javascripts/support/jasmine.yml deleted file mode 100644 index e16e1df3fb..0000000000 --- a/spec/javascripts/support/jasmine.yml +++ /dev/null @@ -1,37 +0,0 @@ -# path to parent directory of src_files -# relative path from Rails.root -# defaults to app/assets/javascripts -src_dir: - -# list of file expressions to include as source files -# relative path from src_dir -src_files: - - "vendor/**/*.{js,coffee}" - - "lib/**/*.{js,coffee}" - - "app/**/*.{js,coffee}" - -# Stylesheets -stylesheets: - - "vendor/**/*.{css,scss}" - - "lib/**/*.{css,scss}" - - "app/**/*.{css,scss}" - -# path to parent directory of spec_files -# relative path from Rails.root -# defaults to spec/javascripts -spec_dir: spec/javascripts - -# list of file expressions to include as helpers into spec runner -# relative path from spec_dir -helpers: - - "helpers/**/*.{js.coffee,js,coffee}" - -# list of file expressions to include as specs into spec runner -# relative path from spec_dir -spec_files: - - "**/*[Ss]pec.{js.coffee,js,coffee}" - -# path to directory of temporary files -# (spec runner and asset cache) -# defaults to tmp/jasmine -tmp_dir: "tmp/jasmine" diff --git a/test/controllers/.keep b/test/controllers/.keep deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/test/fixtures/.keep b/test/fixtures/.keep deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/test/helpers/.keep b/test/helpers/.keep deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/test/integration/.keep b/test/integration/.keep deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/test/mailers/.keep b/test/mailers/.keep deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/test/models/.keep b/test/models/.keep deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/test/test_helper.rb b/test/test_helper.rb deleted file mode 100644 index 9fc373c861..0000000000 --- a/test/test_helper.rb +++ /dev/null @@ -1,7 +0,0 @@ -ENV["RAILS_ENV"] ||= "test" -require File.expand_path('../../config/environment', __FILE__) -require 'rails/test_help' - -class ActiveSupport::TestCase - # Add more helper methods to be used by all tests here... -end diff --git a/testem.yml b/testem.yml deleted file mode 100644 index f97e743041..0000000000 --- a/testem.yml +++ /dev/null @@ -1,9 +0,0 @@ -framework: jasmine2 -test_page: jstest/index.html -src_files: -- app/assets/javascripts/map/** -- vendor/assets/javascripts/** -- jstest/** -launch_in_dev: -- Chrome -- Firefox \ No newline at end of file From 33d8e969006c8a1a5a77912e5afd369c0ee36656 Mon Sep 17 00:00:00 2001 From: David Inga Date: Thu, 26 Jun 2014 11:44:27 +0200 Subject: [PATCH 171/823] recovery testem.yml --- testem.yml | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 testem.yml diff --git a/testem.yml b/testem.yml new file mode 100644 index 0000000000..f97e743041 --- /dev/null +++ b/testem.yml @@ -0,0 +1,9 @@ +framework: jasmine2 +test_page: jstest/index.html +src_files: +- app/assets/javascripts/map/** +- vendor/assets/javascripts/** +- jstest/** +launch_in_dev: +- Chrome +- Firefox \ No newline at end of file From 16de15f946075f948849809c45b9fc08929c6bcc Mon Sep 17 00:00:00 2001 From: Pedro Garcia Date: Thu, 26 Jun 2014 13:11:35 +0200 Subject: [PATCH 172/823] timeline working perfect now --- app/assets/javascripts/map/views/timeline.js | 55 ++++++++++++++------ 1 file changed, 39 insertions(+), 16 deletions(-) diff --git a/app/assets/javascripts/map/views/timeline.js b/app/assets/javascripts/map/views/timeline.js index 9bae293218..04aa01d65e 100644 --- a/app/assets/javascripts/map/views/timeline.js +++ b/app/assets/javascripts/map/views/timeline.js @@ -49,6 +49,16 @@ define([ left:{}, right:{} }; + /** + * Current extent position. + * We use this because we need where the extent is going to be, + * we can't get the values from the handlers because they + * have animation. + */ + this.ext = { + left: 0, + right: 0 + }; this.render(); }, @@ -81,6 +91,8 @@ define([ .range([0, width]) .clamp(true); + this.ext.right = width; + // Set brush and listeners this.brush = d3.svg.brush() .x(this.xscale) @@ -130,10 +142,10 @@ define([ this.handlers.left = this.slider.append("rect") .attr("class", "handle") .attr("transform", "translate(0," + (height / 2 - 6) + ")") - .attr("width", 13) - .attr("height", 13) + .attr("width", 14) + .attr("height", 14) .attr("x", 16) - .attr("y", 0) + .attr("y", -1) .attr("rx", 2) .attr("ry", 2); @@ -251,19 +263,20 @@ define([ // is in the right position. if (this.yearsArr.indexOf(roundValue) < 0 && roundValue > 0) { + // Move domain right this.domain - .attr("x2", this.xscale(roundValue) - 22); + .attr("x2", this.xscale(roundValue) - 16 - 8); // Move trail this.trail - .attr("x1", this.xscale(roundValue) - 16 - 7) - .attr("x2", this.xscale(roundValue) - 16 - 7); + .attr("x1", this.xscale(roundValue) - 16 - 8) + .attr("x2", this.xscale(roundValue) - 16 - 8); // Move && update tooltip this.tooltip .text(roundValue) - .style("left", this.xscale(roundValue) - 16 - 7 + "px"); + .style("left", this.xscale(roundValue) - 16 - 8 + "px"); this.formatXaxis(); @@ -277,7 +290,7 @@ define([ onAnimationBrushEnd: function (event){ var value = this.hiddenBrush.extent()[1]; - var hrl = this.handlers.left.attr("x"); + var hrl = this.ext.left; var trailFrom = Math.round(this.xscale.invert(hrl)) + 1; // +1 year left handler if (value > 0 && value !== trailFrom) { @@ -306,6 +319,13 @@ define([ if (Math.abs(this.xscale(value) - xr - 30) < Math.abs(this.xscale(value) - xl + 16)) { + if (this.ext.left > this.xscale(roundValue)) return; + + this.ext.right = this.xscale(roundValue); + + this.domain + .attr("x1", this.ext.left + 16); + // Move right handler this.handlers.right .transition() @@ -321,6 +341,12 @@ define([ .attr("x2", this.xscale(roundValue) - 30); } else { + if (this.ext.right < this.xscale(roundValue)) return; + this.ext.left = this.xscale(roundValue); + + this.domain + .attr("x2", this.ext.right - 30); + // Move left handler this.handlers.left .transition() @@ -331,25 +357,22 @@ define([ // Move domain left this.domain .transition() - // if it's already transitioning will broke. find a fix! .duration(100) .ease("line") .attr("x1", this.xscale(roundValue) + 16); } - setTimeout(function() { - this.formatXaxis(); - }.bind(this), 100); + this.formatXaxis(); }, formatXaxis: function() { var self = this; d3.select(".xaxis").selectAll("text").filter(function(d, i) { - // TODO: use where its going to move, not where it's already. - // so whe can remove timeout. - if (d > Math.round(self.xscale.invert(self.domain.attr("x1"))) && - d < Math.round(self.xscale.invert(self.domain.attr("x2")))) { + var left = self.ext.left + 16; + var right = self.ext.right + 30; + if (d > Math.round(self.xscale.invert(left)) && + d < Math.round(self.xscale.invert(right))) { d3.select(this).classed("selected", true); } else { d3.select(this).classed("selected", false); From 351898dbe579ac265c5aa11bd104363daccd51f1 Mon Sep 17 00:00:00 2001 From: David Inga Date: Thu, 26 Jun 2014 16:16:17 +0200 Subject: [PATCH 173/823] test and grunt --- .gitignore | 2 ++ .jshintrc | 30 ++++++++++++++++++++++++++++++ Gruntfile.js | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++ package.json | 28 ++++++++++++++++++++++++++++ 4 files changed, 111 insertions(+) create mode 100644 .jshintrc create mode 100644 Gruntfile.js create mode 100644 package.json diff --git a/.gitignore b/.gitignore index 966bf322ab..46aca20fd2 100644 --- a/.gitignore +++ b/.gitignore @@ -25,6 +25,8 @@ .rvmrc .env .idea/ +.grunt +node_modules # Ingore Sublime project files: *.sublime-* \ No newline at end of file diff --git a/.jshintrc b/.jshintrc new file mode 100644 index 0000000000..38aa3284c5 --- /dev/null +++ b/.jshintrc @@ -0,0 +1,30 @@ +{ + "bitwise": true, + "browser": true, + "jquery": true, + "camelcase": false, + "curly": true, + "eqeqeq": true, + "esnext": true, + "expr": true, + "immed": true, + "indent": 2, + "latedef": true, + "newcap": true, + "noarg": true, + "node": true, + "noempty": true, + "nonstandard": true, + "quotmark": "single", + "regexp": true, + "smarttabs": true, + "strict": true, + "trailing": true, + "undef": true, + "unused": true, + "expr": true, + + "predef": [ + "define" + ] +} \ No newline at end of file diff --git a/Gruntfile.js b/Gruntfile.js new file mode 100644 index 0000000000..729b34ec27 --- /dev/null +++ b/Gruntfile.js @@ -0,0 +1,51 @@ +'use strict'; + +module.exports = function(grunt) { + + require('load-grunt-tasks')(grunt, { + pattern: ['grunt-*', '!grunt-template-jasmine-requirejs'] + }); + require('time-grunt')(grunt); + + grunt.initConfig({ + + root: { + app: 'app/assets' + }, + + jshint: { + options: { + jshintrc: '.jshintrc', + reporter: require('jshint-stylish') + }, + all: [ + 'Gruntfile.js', + '<%= root.app %>/javascripts/map/{,*/}{,*/}*.js' + ] + }, + + testem: { + jasmine: { + src: '<%= root.app %>/javascripts/map/{,*/}{,*/}*.js' + } + }, + + watch: { + options: { + nospawn: true + }, + scripts: { + files: '<%= jshint.all %>', + tasks: ['jshint', 'testem'] + } + } + + }); + + grunt.registerTask('default', [ + 'jshint', + 'testem', + 'watch' + ]); + +}; diff --git a/package.json b/package.json new file mode 100644 index 0000000000..d897469931 --- /dev/null +++ b/package.json @@ -0,0 +1,28 @@ +{ + "name": "globalforestwatch", + "version": "1.1.0", + "description": "Global Forest Watch", + "main": "Gruntfile.js", + "scripts": { + "test": "testem" + }, + "repository": { + "type": "git", + "url": "https://github.com/Vizzuality/gfw.git" + }, + "author": "Simbiótica ", + "license": "MIT", + "bugs": { + "url": "https://github.com/Vizzuality/gfw/issues" + }, + "homepage": "https://github.com/Vizzuality/gfw", + "devDependencies": { + "grunt": "^0.4.5", + "grunt-contrib-jshint": "^0.10.0", + "grunt-contrib-testem": "^0.5.16", + "grunt-contrib-watch": "^0.6.1", + "jshint-stylish": "^0.2.0", + "load-grunt-tasks": "^0.6.0", + "time-grunt": "^0.3.2" + } +} From d58d8e81a2c133406520090f9c85e9ab820ed83b Mon Sep 17 00:00:00 2001 From: Pedro Garcia Date: Thu, 26 Jun 2014 16:57:42 +0200 Subject: [PATCH 174/823] stuff before merge --- app/assets/javascripts/map/views/layers/loss.js | 8 +++++++- app/assets/javascripts/map/views/timeline.js | 12 ++++++------ 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/app/assets/javascripts/map/views/layers/loss.js b/app/assets/javascripts/map/views/layers/loss.js index 15828ba937..2279e5cd0e 100644 --- a/app/assets/javascripts/map/views/layers/loss.js +++ b/app/assets/javascripts/map/views/layers/loss.js @@ -22,7 +22,13 @@ define([ this.timeline = new Timeline({ dateRange: [moment([2001]), moment([2013])], - layerName: 'loss' + layerName: 'loss', + xAxis: { + months: { + enabled: true, + steps: true + } + } }); }, diff --git a/app/assets/javascripts/map/views/timeline.js b/app/assets/javascripts/map/views/timeline.js index 04aa01d65e..8c83cfc5b7 100644 --- a/app/assets/javascripts/map/views/timeline.js +++ b/app/assets/javascripts/map/views/timeline.js @@ -49,6 +49,7 @@ define([ left:{}, right:{} }; + /** * Current extent position. * We use this because we need where the extent is going to be, @@ -320,7 +321,6 @@ define([ if (Math.abs(this.xscale(value) - xr - 30) < Math.abs(this.xscale(value) - xl + 16)) { if (this.ext.left > this.xscale(roundValue)) return; - this.ext.right = this.xscale(roundValue); this.domain @@ -369,10 +369,10 @@ define([ var self = this; d3.select(".xaxis").selectAll("text").filter(function(d, i) { - var left = self.ext.left + 16; - var right = self.ext.right + 30; - if (d > Math.round(self.xscale.invert(left)) && - d < Math.round(self.xscale.invert(right))) { + var x1 = self.ext.left + 16; + var x2 = self.ext.right + 30; + if (d > Math.round(self.xscale.invert(x1)) && + d < Math.round(self.xscale.invert(x2))) { d3.select(this).classed("selected", true); } else { d3.select(this).classed("selected", false); @@ -387,7 +387,7 @@ define([ onBrushEnd: function(event) { var startYear = Math.round(this.xscale.invert(this.handlers.left.attr("x"))); var endYear = Math.round(this.xscale.invert(this.handlers.right.attr("x"))); - + // give time to finish animations. setTimeout(function() { this.updateTimelineDate([moment([startYear]), moment([endYear])]); }.bind(this), 100); From 657258b4673137db27e3ffe025e45e84181d22aa Mon Sep 17 00:00:00 2001 From: David Inga Date: Thu, 26 Jun 2014 17:33:11 +0200 Subject: [PATCH 175/823] jshint --- .jshintrc | 5 +-- app/assets/javascripts/map/app.js | 7 ++-- .../javascripts/map/collections/layers.js | 36 ++++++++++++------- app/assets/javascripts/map/main.js | 4 ++- 4 files changed, 32 insertions(+), 20 deletions(-) diff --git a/.jshintrc b/.jshintrc index 38aa3284c5..641699e030 100644 --- a/.jshintrc +++ b/.jshintrc @@ -25,6 +25,7 @@ "expr": true, "predef": [ - "define" + "define", + "google" ] -} \ No newline at end of file +} diff --git a/app/assets/javascripts/map/app.js b/app/assets/javascripts/map/app.js index 166798aec7..44ebf82e19 100644 --- a/app/assets/javascripts/map/app.js +++ b/app/assets/javascripts/map/app.js @@ -1,11 +1,12 @@ /** * The application module. - * + * * @return singleton instance of App class. */ define([ 'Class' ], function (Class) { + 'use strict'; var App = Class.extend({ init: function() { @@ -15,7 +16,5 @@ define([ var app = new App(); - - return app; -}); \ No newline at end of file +}); diff --git a/app/assets/javascripts/map/collections/layers.js b/app/assets/javascripts/map/collections/layers.js index b5258dddb0..91f4959d94 100644 --- a/app/assets/javascripts/map/collections/layers.js +++ b/app/assets/javascripts/map/collections/layers.js @@ -1,7 +1,7 @@ /** * The layer collection module. - * - * @return singleton instance of LayerCollection class (extends + * + * @return singleton instance of LayerCollection class (extends * Backbone.CartoDB.CartoDBCollection). */ define([ @@ -12,39 +12,49 @@ define([ 'mps', 'gmap', 'presenter' -], function ($, _, Backbone, BackboneCartoDB, mps, gmap, presenter) { +], function($, _, Backbone) { + 'use strict'; - var LayerCollection = Backbone.CartoDB({user: 'wri-01'}).CartoDBCollection.extend({ + var LayerCollection = Backbone.CartoDB({ + user: 'wri-01' + }).CartoDBCollection.extend({ sql: function() { - return ['SELECT cartodb_id AS id, slug, title, title_color, subtitle, sublayer, table_name, source, category_color, category_slug, category_name, external, zmin, zmax, ST_XMAX(the_geom) AS xmax,', + return [ + 'SELECT cartodb_id AS id, slug, title, title_color, subtitle, sublayer, table_name, source, category_color, category_slug, category_name, external, zmin, zmax, ST_XMAX(the_geom) AS xmax,', 'ST_XMIN(the_geom) AS xmin, ST_YMAX(the_geom) AS ymax, ST_YMIN(the_geom) AS ymin, tileurl, true AS visible', 'FROM layerinfo_dev_copy', 'WHERE display = TRUE ORDER BY displaylayer, title ASC'].join(' '); }, getBaselayers: function() { - return _.where(this.toJSON(), {category_name: 'Forest change'}) + return _.where(this.toJSON(), { + category_name: 'Forest change' + }); }, getBaselayer: function(layerName) { - var layer = _.where(this.toJSON(), {category_name: 'Forest change', - slug: layerName}); + var layer = _.where(this.toJSON(), { + category_name: 'Forest change', + slug: layerName + }); if (layer) { - return layer[0]; + return layer[0]; } }, getSublayers: function() { return _.filter(this.toJSON(), function(layer) { - return layer.category_name != 'Forest change'; + return layer.category_name !== 'Forest change'; }); }, getSublayer: function(layerName) { - var layer = _.where(this.getSublayers(), {slug: layerName}); + var layer = _.where(this.getSublayers(), { + slug: layerName + }); if (layer) { - return layer[0]; + return layer[0]; } } }); @@ -53,4 +63,4 @@ define([ return layerCollection; -}); \ No newline at end of file +}); diff --git a/app/assets/javascripts/map/main.js b/app/assets/javascripts/map/main.js index b6144a51dc..59a8209740 100644 --- a/app/assets/javascripts/map/main.js +++ b/app/assets/javascripts/map/main.js @@ -1,3 +1,5 @@ +'use strict'; + // Application entry point require([ 'utils', @@ -17,4 +19,4 @@ require([ window.mps = mps; window.router = router; }); -}); \ No newline at end of file +}); From 1cb14972add1184afb633dc81e3d4e8bbba6eb1a Mon Sep 17 00:00:00 2001 From: Pedro Garcia Date: Thu, 26 Jun 2014 17:48:31 +0200 Subject: [PATCH 176/823] deleting old stuff --- app/assets/javascripts/map/main.js | 6 +- app/assets/javascripts/map/mediator.js | 128 -------------- app/assets/javascripts/map/presenter.js | 162 ------------------ app/assets/javascripts/map/router.js | 3 +- .../javascripts/map/services/PlaceService.js | 5 +- .../map/views/layers/core/canvasLayer.js | 147 ---------------- .../javascripts/map/views/layers/loss.js | 66 ------- 7 files changed, 7 insertions(+), 510 deletions(-) delete mode 100644 app/assets/javascripts/map/mediator.js delete mode 100644 app/assets/javascripts/map/presenter.js delete mode 100644 app/assets/javascripts/map/views/layers/core/canvasLayer.js delete mode 100644 app/assets/javascripts/map/views/layers/loss.js diff --git a/app/assets/javascripts/map/main.js b/app/assets/javascripts/map/main.js index b6144a51dc..ee1ad3962d 100644 --- a/app/assets/javascripts/map/main.js +++ b/app/assets/javascripts/map/main.js @@ -1,15 +1,15 @@ -// Application entry point +/** + * Application entry point. + */ require([ 'utils', 'backbone', 'underscore' ], function (utils, Backbone, _) { - console.log('Main entry point...'); require(['router', 'services/AnalysisService', 'mps'], function(router, as, mps) { if (!Backbone.History.started) { - console.log('Backbone.history.start'); Backbone.history.start({pushState: true}); } // For dev diff --git a/app/assets/javascripts/map/mediator.js b/app/assets/javascripts/map/mediator.js deleted file mode 100644 index 3e9f1c3ad8..0000000000 --- a/app/assets/javascripts/map/mediator.js +++ /dev/null @@ -1,128 +0,0 @@ -/** - * The mediator module. - * - * Mediator listens to events fired by the Presenter and renders the - * appropriate views. - * - * @return singleton instance of Mediator class. - */ -define([ - 'underscore', - 'backbone', - 'mps', - 'Class', - 'presenter', - 'collections/layers', - 'views/map', - 'views/layersMenu', - 'views/legend', - 'views/layers/loss', - 'views/layers/gain', - 'views/layers/forest', - 'views/layers/imazon' -], function (_, Backbone, mps, Class, presenter, layersCollection, map, layersMenu, legend, - LossLayer, GainLayer, ForestLayer, ImazonLayer) { - - var Mediator = Class.extend({ - init: function() { - var self = this; - - // Listen to presenter events - presenter.on('change:zoom', this.updateZoom, this); - presenter.on('change:latLng', this.updateCenter, this); - // presenter.on('change:iso', this.mapChange, this); - presenter.on('change:maptype', this.updateMapType, this); - presenter.on('change:baselayers', this.updateBaselayers, this); - presenter.on('change:sublayers', this.updateSublayers, this); - presenter.on('change:timelineDate', this.updateBaselayersTiles, this); - presenter.on('change:baselayers', legend.update, this); - presenter.on('change:sublayers', legend.update, this); - - this.collections = {}; - this.layersInstances = {}; - }, - - layersViews: { - loss: LossLayer, - umd_tree_loss_gain: GainLayer, - imazon: ImazonLayer, - forest2000: ForestLayer - }, - - updateBaselayers: function() { - var currentBaselayers = presenter.get('baselayers'), - allBaselayers = layersCollection.getBaselayers(); - - // Render current baselayers - for (var i = 0; i < currentBaselayers.length; i++) { - var layerName = currentBaselayers[i]; - this.createLayerView(layerName); - }; - - // Remove baselayers - for (var i = 0; i < allBaselayers.length; i++) { - var layer = allBaselayers[i]; - if (currentBaselayers.indexOf(layer.slug) < 0 && - this.layersInstances[layer.slug]) { - this.layersInstances[layer.slug].removeLayer(); - } - }; - }, - - updateSublayers: function() { - var currentSublayers = presenter.get('sublayers'), - allSublayers = layersCollection.getSublayers(); - - // Render sublayers - for (var i = 0; i < currentSublayers.length; i++) { - var layerId = currentSublayers[i], - layer = layersCollection.findWhere({ id: layerId }), - layerName = layer.get('slug'); - - this.createLayerView(layerName); - }; - - // Remove sublayers - for (var i = 0; i < allSublayers.length; i++) { - var layer = allSublayers[i]; - if (currentSublayers.indexOf(layer.id) < 0 && - this.layersInstances[layer.slug]) { - this.layersInstances[layer.slug].removeLayer(); - } - }; - }, - - createLayerView: function(layerName) { - if (!this.layersInstances[layerName]) { - this.layersInstances[layerName] = new this.layersViews[layerName](); - } - this.layersInstances[layerName].render(); - }, - - updateBaselayersTiles: function() { - var currentBaselayers = presenter.get('baselayers'); - - for (var i = 0; i < currentBaselayers.length; i++) { - var layerName = currentBaselayers[i]; - this.layersInstances[layerName].updateTiles(); - }; - }, - - updateZoom: function() { - map.updateZoom(Number(presenter.get('zoom'))); - }, - - updateCenter: function() { - map.updateCenter(presenter.get('latLng')); - }, - - updateMapType: function() { - map.updateMapType(presenter.get('maptype')); - } - }); - - var mediator = new Mediator(); - - return mediator; - -}); \ No newline at end of file diff --git a/app/assets/javascripts/map/presenter.js b/app/assets/javascripts/map/presenter.js deleted file mode 100644 index e1ca0e2f39..0000000000 --- a/app/assets/javascripts/map/presenter.js +++ /dev/null @@ -1,162 +0,0 @@ -/** - * The presenter module. - * - * Presenter fires events in response to changing URL routes. - * - * @return singleton instance of Presenter class. - */ -define([ - 'backbone', - 'mps', - 'collections/layers' -], function (Backbone, mps, layersCollection) { - - var Presenter = Backbone.Model.extend({ - - baselayersOpts: { - allowedCombined: [ - ['loss', 'umd_tree_loss_gain'] - ] - }, - - initialize: function() { - var self = this; - - this.on('change', this.updateUrl, this); - - mps.subscribe('presenter/toggle-layer', function(layerName) { - self.toggleLayer(layerName); - }); - }, - - /** - * Set the presenter object with the passed attributes. - * TODO: Proper validation. Just validate attrs from the url, not on every - * change. - * @param {array} Router params object. - */ - setFromUrl: function(attrs) { - var baselayers = null, - sublayers = (attrs.sublayers) ? attrs.sublayers.split(',') : null, - latLng = (attrs.lat && attrs.lng) ? [attrs.lat, attrs.lng] : null; - - if (attrs.baselayers && - this.validateBaselayers(attrs.baselayers.split(','))) { - baselayers = attrs.baselayers.split(','); - } - - if (sublayers) { - for (var i = 0; i < sublayers.length; i++) { - sublayers[i] = Number(sublayers[i]); - }; - } - - var results = { - zoom: attrs.zoom || 3, - latLng: latLng || [15.00, 27.00], - iso: attrs.iso || 'ALL', - maptype: attrs.maptype || 'terrain', - baselayers: baselayers || ['loss', 'umd_tree_loss_gain'], - sublayers: sublayers || [] - }; - - this.set(results); - }, - - /** - * Update location with the presenter status. Calls router navigate. - * - */ - updateUrl: function() { - var attrs = { - zoom: this.get('zoom'), - lat: this.get('latLng')[0].toFixed(2), - lng: this.get('latLng')[1].toFixed(2), - iso: this.get('iso'), - maptype: this.get('maptype'), - baselayers: this.get('baselayers'), - sublayers: this.get('sublayers') - }; - - var place = { - path: _.values(attrs).join('/'), - trigger: false - }; - mps.publish('navigate', [place]); - }, - - /** - * Toggle a layer on the presenter - * - * @param {string} LayerName. - */ - toggleLayer: function(layerName) { - if (layersCollection.getBaselayer(layerName)) { - this.toggleBaselayer(layerName); - } else if (layersCollection.getSublayer(layerName)) { - this.toggleSublayer(layerName,layersCollection.getSublayer(layerName).id); - } - }, - - toggleBaselayer: function(layerName) { - var currentBaselayers = _.clone(presenter.get('baselayers')), - layerIndex = currentBaselayers.indexOf(layerName); - - if (layerIndex > -1) { - if (currentBaselayers.length > 1) { - currentBaselayers.splice(layerIndex, 1); - presenter.set('baselayers', currentBaselayers); - } - } else { - var combined = presenter.get('baselayers').concat(layerName); - - if (this.validateBaselayers(combined)) { - presenter.set('baselayers', combined); - } else { - presenter.set('baselayers', [layerName]); - } - } - }, - - toggleSublayer: function(layerName, layerId) { - var currentSublayers = _.clone(presenter.get('sublayers')); - layerIndex = currentSublayers.indexOf(layerId); - - if (layerIndex > -1) { - currentSublayers.splice(layerIndex, 1); - presenter.set('sublayers', currentSublayers); - } else { - var combined = presenter.get('sublayers').concat(layerId); - presenter.set('sublayers', combined); - } - }, - - /** - * Validates baselayers are valid. - * - * @param {array} Baselayers array. - */ - validateBaselayers: function(baselayersArr) { - var valid = false, - allowedCombined = this.baselayersOpts.allowedCombined; - - for (var i = 0; i < allowedCombined.length; i++) { - var layersArr = allowedCombined[i]; - - if (baselayersArr.length === 1 || - ($(baselayersArr).not(layersArr).length === 0 && - $(layersArr).not(baselayersArr).length === 0)) { - valid = true; - } - }; - - return valid; - }, - - }); - - var presenter = new Presenter(); - - return presenter; - -}); \ No newline at end of file diff --git a/app/assets/javascripts/map/router.js b/app/assets/javascripts/map/router.js index 455aa82f1e..92702934f4 100644 --- a/app/assets/javascripts/map/router.js +++ b/app/assets/javascripts/map/router.js @@ -27,10 +27,9 @@ define([ }, initialize: function() { - console.log('router.initialize()'); - Backbone.Router.prototype.initialize.call(this); _.bindAll(this, 'navigateTo'); mps.subscribe('navigate', this.navigateTo); + this.setMapSize(); }, map: function(zoom, lat, lng, iso, maptype, baselayers, sublayers) { diff --git a/app/assets/javascripts/map/services/PlaceService.js b/app/assets/javascripts/map/services/PlaceService.js index 8baa9dfa1e..1b9f596ed7 100644 --- a/app/assets/javascripts/map/services/PlaceService.js +++ b/app/assets/javascripts/map/services/PlaceService.js @@ -90,7 +90,7 @@ define([ function (name) { return {slug: name, category_slug: 'forest_clearing'}; }); - + // Create sublayer filters sublayers = params.sublayers ? params.sublayers.split(',') : []; subWhere = _.map( @@ -100,12 +100,13 @@ define([ }); // Combine layer filters with order preserved - where = _.union(baseWhere, subWhere); + where = _.union(baseWhere, subWhere); // Get layers from MapLayerService this.mapLayerService.getLayers( where, _.bind(function(layers) { + console.log(layers); this.mapLayers = layers; callback(this.mapLayers); }, this), diff --git a/app/assets/javascripts/map/views/layers/core/canvasLayer.js b/app/assets/javascripts/map/views/layers/core/canvasLayer.js deleted file mode 100644 index a473e73e15..0000000000 --- a/app/assets/javascripts/map/views/layers/core/canvasLayer.js +++ /dev/null @@ -1,147 +0,0 @@ -/** - * The HTML5 Canvas map layer module. - * - * @return CanvasLayer class (extends Backbone.View). - */ -define([ - 'backbone', - 'mps' -], function(Backbone, mps) { - - var CanvasLayer = Backbone.View.extend({ - - initialize: function () { - this.tileSize = new google.maps.Size(256, 256); - this.tiles = {}; - this.rendered = false; - }, - - render: function() { - if (!this.rendered) { - mps.publish('map/add-layer', [this]); - this.rendered = true; - } - }, - - getTile: function(coord, zoom, ownerDocument) { - var canvas = ownerDocument.createElement('canvas'); - canvas.style.border = "none"; - canvas.style.margin = "0"; - canvas.style.padding = "0"; - - var ctx = canvas.getContext('2d'); - ctx.width = canvas.width = this.tileSize.width; - ctx.height = canvas.height = this.tileSize.height; - - var tileId = coord.x + '_' + coord.y + '_' + zoom; - canvas.setAttribute('id', tileId); - - if (tileId in this.tiles) { - delete this.tiles[tileId]; - } - - this.tiles[tileId] = canvas; - this.canvasSetup(canvas, coord, zoom); - - return canvas; - }, - - canvasSetup: function(canvas, coord, zoom) { - var self = this, - xhr = new XMLHttpRequest(), - ctx = canvas.getContext('2d'); - - var x = coord.x, - y = coord.y, - z = zoom; - - if (zoom > this.dataMaxZoom) { - x = Math.floor(coord.x / (Math.pow(2, zoom - this.dataMaxZoom))); - y = Math.floor(coord.y / (Math.pow(2, zoom - this.dataMaxZoom))); - z = this.dataMaxZoom; - } else { - y = (y > Math.pow(2, z) ? y % Math.pow(2, z) : y); - if (x >= Math.pow(2, z)) { - x = x % Math.pow(2, z); - } else if (x < 0) { - x = Math.pow(2, z) - Math.abs(x); - } - } - - var url = this.url.replace('%z', z).replace('%x', x).replace('%y', y); - - xhr.onload = function () { - var url = URL.createObjectURL(this.response), - image = new Image(); - - image.onload = function () { - image.crossOrigin = ''; - - canvas.image = image; - canvas.coord = coord; - canvas.coord.z = zoom; - - ctx.drawImage(image, 0, 0); - self.filterTile(canvas); - - URL.revokeObjectURL(url); - }; - - image.src = url; - }; - - xhr.open('GET', url, true); - xhr.responseType = 'blob'; - xhr.send(); - }, - - removeLayer: function() { - if (this.rendered) { - mps.publish('map/remove-layer', [this.name]); - this.rendered = false; - } - }, - - filterCanvasImage: function() { - }, - - updateTiles: function() { - for(var i in this.tiles) { - this.filterTile(this.tiles[i]); - } - }, - - filterTile: function(canvas) { - var ctx = canvas.getContext('2d'); - coord = canvas.coord; - - if (canvas.coord) { - var zsteps = coord.z - this.dataMaxZoom; - - if (zsteps > 0) { - ctx.imageSmoothingEnabled = false; - ctx.mozImageSmoothingEnabled = false; - ctx.webkitImageSmoothingEnabled = false; - - var srcX = 256 / Math.pow(2, zsteps) * (coord.x % Math.pow(2, zsteps)), - srcY = 256 / Math.pow(2, zsteps) * (coord.y % Math.pow(2, zsteps)), - srcW = 256 / Math.pow(2, zsteps), - srcH = 256 / Math.pow(2, zsteps); - - ctx.clearRect(0, 0, 256, 256); - ctx.drawImage(canvas.image, srcX, srcY, srcW, srcH, 0, 0, 256, 256); - } else { - try { - ctx.drawImage(canvas.image, 0, 0); - } catch(err) { } - } - - var I = ctx.getImageData(0, 0, canvas.width, canvas.height); - this.filterCanvasImage(I.data, ctx.width, ctx.height); - ctx.putImageData(I,0,0); - } - } - }); - - return CanvasLayer; -}); \ No newline at end of file diff --git a/app/assets/javascripts/map/views/layers/loss.js b/app/assets/javascripts/map/views/layers/loss.js deleted file mode 100644 index 2279e5cd0e..0000000000 --- a/app/assets/javascripts/map/views/layers/loss.js +++ /dev/null @@ -1,66 +0,0 @@ -/** - * The loss layer module for use on canvas. - * - * @return LossLayer class (extends CanvasLayer) - */ -define([ - 'backbone', - 'mps', - 'presenter', - 'moment', - 'views/layers/core/canvasLayer', - 'views/timeline' -], function(Backbone, mps, presenter, moment, CanvasLayer, Timeline) { - - var LossLayer = CanvasLayer.extend({ - - initialize: function() { - this.dataMaxZoom = 12; - this.name = "loss"; - this.url = 'http://earthengine.google.org/static/hansen_2013/gfw_loss_year/%z/%x/%y.png'; - LossLayer.__super__.initialize.apply(this); - - this.timeline = new Timeline({ - dateRange: [moment([2001]), moment([2013])], - layerName: 'loss', - xAxis: { - months: { - enabled: true, - steps: true - } - } - }); - }, - - filterCanvasImage: function(imgdata, w, h) { - var components = 4, - z = presenter.get('zoom'), - timelineDate = presenter.get('timelineDate') || this.timeline.opts.dateRange; - - timelineDate = [timelineDate[0].year(), timelineDate[1].year()]; - - for(var i = 0; i < w; ++i) { - for(var j = 0; j < h; ++j) { - var pixelPos = (j * w + i) * components, - yearLoss = imgdata[pixelPos], - yearStart = timelineDate[0], - yearEnd = timelineDate[1]; - - yearLoss = 2000 + yearLoss; - - if (imgdata[pixelPos + 1] > 10 && (yearLoss >= yearStart && yearLoss < yearEnd)) { - imgdata[pixelPos] = 220; - imgdata[pixelPos + 1] = 102; - imgdata[pixelPos + 2] = 153; - imgdata[pixelPos + 3] = (z < 13) ? (12/z) * 255 : 255; - } else { - imgdata[pixelPos + 3] = 0; - } - } - } - } - }); - - return LossLayer; - -}); \ No newline at end of file From 74a7a92b37d674b8375c87d23c06cce3c4732d64 Mon Sep 17 00:00:00 2001 From: David Inga Date: Thu, 26 Jun 2014 17:49:14 +0200 Subject: [PATCH 177/823] timeline jshint --- app/assets/javascripts/map/gmap.js | 12 +- app/assets/javascripts/map/main.js | 5 +- app/assets/javascripts/map/views/legend.js | 4 +- app/assets/javascripts/map/views/timeline.js | 178 ++++++++++--------- spec/spec_helper.rb | 2 +- 5 files changed, 102 insertions(+), 99 deletions(-) diff --git a/app/assets/javascripts/map/gmap.js b/app/assets/javascripts/map/gmap.js index 8cc5444c7c..7ffa0d7e9b 100644 --- a/app/assets/javascripts/map/gmap.js +++ b/app/assets/javascripts/map/gmap.js @@ -1,13 +1,13 @@ /** * The Google Maps module. - * + * * @return object used to async initialize Google Maps API */ define([ 'jquery', 'underscore' ], function ($, _) { - + var libs = [ { name: 'maps', @@ -26,7 +26,7 @@ define([ /** * Loads the Google Maps API and then the CartoDB API. Fires the callback * after both are loaded. - * + * * @param cb The callback function. */ init: function (cb) { @@ -38,8 +38,8 @@ define([ // Load the jsapi and then grab each lib. - require(['https://www.google.com/jsapi?callback=?' + - '&key=AIzaSyDJdVhfQhecwp0ngAGzN9zwqak8FaEkSTA'], function () { + require(['https://www.google.com/jsapi?callback=?' + + '&key=AIzaSyDJdVhfQhecwp0ngAGzN9zwqak8FaEkSTA'], function () { _.each(libs, function (lib) { google.load(lib.name, lib.version, _.extend(lib.options, { callback: done })); @@ -48,4 +48,4 @@ define([ }); } }; -}); \ No newline at end of file +}); diff --git a/app/assets/javascripts/map/main.js b/app/assets/javascripts/map/main.js index 59a8209740..dc18f4aec5 100644 --- a/app/assets/javascripts/map/main.js +++ b/app/assets/javascripts/map/main.js @@ -3,9 +3,8 @@ // Application entry point require([ 'utils', - 'backbone', - 'underscore' -], function (utils, Backbone, _) { + 'backbone' +], function (utils, Backbone) { console.log('Main entry point...'); require(['router', 'services/AnalysisService', 'mps'], diff --git a/app/assets/javascripts/map/views/legend.js b/app/assets/javascripts/map/views/legend.js index fda777850b..27036af00c 100644 --- a/app/assets/javascripts/map/views/legend.js +++ b/app/assets/javascripts/map/views/legend.js @@ -12,6 +12,8 @@ define([ 'text!map/templates/legend.html' ], function(Backbone, _, presenter, mps, Widget, legendTpl) { + 'use strict'; + var Legend = Widget.extend({ className: 'widget legend', @@ -37,4 +39,4 @@ define([ return legend; -}); \ No newline at end of file +}); diff --git a/app/assets/javascripts/map/views/timeline.js b/app/assets/javascripts/map/views/timeline.js index 04aa01d65e..f851dc2f74 100644 --- a/app/assets/javascripts/map/views/timeline.js +++ b/app/assets/javascripts/map/views/timeline.js @@ -2,7 +2,7 @@ * The timeline module. * * Timeline for all layers configured by setting layer-specific options. - * + * * @return Timeline class (extends Backbone.View). */ define([ @@ -13,6 +13,8 @@ define([ 'text!map/templates/timeline.html' ], function(Backbone, presenter, moment, d3, timelineTpl) { + 'use strict'; + var Timeline = Backbone.View.extend({ className: 'timeline timeline-date-range', @@ -97,103 +99,103 @@ define([ this.brush = d3.svg.brush() .x(this.xscale) .extent([0, 0]) - .on("brush", function() { + .on('brush', function() { self.onBrush(this) }) - .on("brushend", function() { + .on('brushend', function() { self.onBrushEnd(this); }); // Set top svg - this.svg = d3.select(this.$time[0]).append("svg") - .attr("width", width + margin.left + margin.right) - .attr("height", height + margin.top + margin.bottom) - .append("g") - .attr("transform", "translate(" + margin.left + "," + margin.top + ")"); + this.svg = d3.select(this.$time[0]).append('svg') + .attr('width', width + margin.left + margin.right) + .attr('height', height + margin.top + margin.bottom) + .append('g') + .attr('transform', 'translate(' + margin.left + ',' + margin.top + ')'); // Bar, years - this.svg.append("g") - .attr("class", "xaxis") - .attr("transform", "translate(0, " + (height / 2) + ")") + this.svg.append('g') + .attr('class', 'xaxis') + .attr('transform', 'translate(0, ' + (height / 2) + ')') .call(d3.svg.axis() .scale(this.xscale) - .orient("top") + .orient('top') .ticks(this.opts.dateRange[1].year() - this.opts.dateRange[0].year()) .tickFormat(function(d) {return String(d); }) .tickSize(0) .tickPadding(-4)) - .select(".domain").remove(); + .select('.domain').remove(); - this.svg.select(".xaxis").selectAll("g.line").remove(); + this.svg.select('.xaxis').selectAll('g.line').remove(); - this.svg.select(".xaxis").selectAll("g.tick") - .insert("rect", ":first-child") - .attr("width", 30) - .attr("height", 12) - .attr("x", -15) - .attr("y", -5) - .attr("fill", "white"); + this.svg.select('.xaxis').selectAll('g.tick') + .insert('rect', ':first-child') + .attr('width', 30) + .attr('height', 12) + .attr('x', -15) + .attr('y', -5) + .attr('fill', 'white'); // Handlers - this.slider = this.svg.append("g") - .attr("class", "slider") + this.slider = this.svg.append('g') + .attr('class', 'slider') .call(this.brush); - this.handlers.left = this.slider.append("rect") - .attr("class", "handle") - .attr("transform", "translate(0," + (height / 2 - 6) + ")") - .attr("width", 14) - .attr("height", 14) - .attr("x", 16) - .attr("y", -1) - .attr("rx", 2) - .attr("ry", 2); + this.handlers.left = this.slider.append('rect') + .attr('class', 'handle') + .attr('transform', 'translate(0,' + (height / 2 - 6) + ')') + .attr('width', 14) + .attr('height', 14) + .attr('x', 16) + .attr('y', -1) + .attr('rx', 2) + .attr('ry', 2); this.handlers.right = this.handlers.left .select(function() { return this.parentNode.appendChild(this.cloneNode(true)); }) .attr('x', this.xscale(this.opts.dateRange[1].year()) - 30); - this.slider.select(".background") - .style("cursor", "pointer") - .attr("height", height); + this.slider.select('.background') + .style('cursor', 'pointer') + .attr('height', height); // Tipsy - this.tipsy = this.svg.append("g") - .attr("class", "tipsy") - .style("visibility", "hidden"); - - this.trail = this.tipsy.append("svg:line") - .attr("class", "trail") - .attr("x1", this.handlers.right.attr("x")) - .attr("x2", this.handlers.right.attr("x")) - .attr("y1", 0) - .attr("y2", height); - - this.tooltip = d3.select(this.$time[0]).append("div") - .attr("class", "tooltip") - .style("visibility", "hidden") - .style("left", this.handlers.right.attr("x") + 'px') + this.tipsy = this.svg.append('g') + .attr('class', 'tipsy') + .style('visibility', 'hidden'); + + this.trail = this.tipsy.append('svg:line') + .attr('class', 'trail') + .attr('x1', this.handlers.right.attr('x')) + .attr('x2', this.handlers.right.attr('x')) + .attr('y1', 0) + .attr('y2', height); + + this.tooltip = d3.select(this.$time[0]).append('div') + .attr('class', 'tooltip') + .style('visibility', 'hidden') + .style('left', this.handlers.right.attr('x') + 'px') .text(this.opts.dateRange[0].year()); // Hidden brush for the animation this.hiddenBrush = d3.svg.brush() .x(this.xscale) .extent([0, 0]) - .on("brush", function() { + .on('brush', function() { self.onAnimationBrush(this) }) - .on("brushend", function() { + .on('brushend', function() { self.onAnimationBrushEnd(this); }); - this.svg.selectAll(".extent,.resize") + this.svg.selectAll('.extent,.resize') .remove(); - this.domain = this.svg.select(".xaxis") - .insert("svg:line", ":first-child") - .attr("class", "domain") - .attr("x1", this.handlers.left.attr("x")) - .attr("x2", this.handlers.right.attr("x")); + this.domain = this.svg.select('.xaxis') + .insert('svg:line', ':first-child') + .attr('class', 'domain') + .attr('x1', this.handlers.left.attr('x')) + .attr('x2', this.handlers.right.attr('x')); this.formatXaxis(); }, @@ -221,8 +223,8 @@ define([ * Play the timeline by extending hiddenBrush with d3 animation. */ animate: function() { - var hlx = this.handlers.left.attr("x"); - var hrx = this.handlers.right.attr("x"); + var hlx = this.handlers.left.attr('x'); + var hrx = this.handlers.right.attr('x'); var trailFrom = Math.round(this.xscale.invert(hlx)) + 1; // +1 year left handler var trailTo = Math.round(this.xscale.invert(hrx)); @@ -244,7 +246,7 @@ define([ .call(this.hiddenBrush.event) .transition() .duration(speed) - .ease("line") + .ease('line') .call(this.hiddenBrush.extent([trailFrom, trailTo])) .call(this.hiddenBrush.event); }, @@ -266,22 +268,22 @@ define([ // Move domain right this.domain - .attr("x2", this.xscale(roundValue) - 16 - 8); + .attr('x2', this.xscale(roundValue) - 16 - 8); // Move trail this.trail - .attr("x1", this.xscale(roundValue) - 16 - 8) - .attr("x2", this.xscale(roundValue) - 16 - 8); + .attr('x1', this.xscale(roundValue) - 16 - 8) + .attr('x2', this.xscale(roundValue) - 16 - 8); // Move && update tooltip this.tooltip .text(roundValue) - .style("left", this.xscale(roundValue) - 16 - 8 + "px"); + .style('left', this.xscale(roundValue) - 16 - 8 + 'px'); this.formatXaxis(); // Update timeline - var startYear = Math.round(this.xscale.invert(this.handlers.left.attr("x"))); + var startYear = Math.round(this.xscale.invert(this.handlers.left.attr('x'))); this.updateTimelineDate([moment([startYear]), moment([roundValue])]); this.yearsArr.push(roundValue); @@ -307,9 +309,9 @@ define([ onBrush: function(event) { var value = this.xscale.invert(d3.mouse(event)[0]); var roundValue = Math.round(value); - - var xl = this.handlers.left.attr("x"); - var xr = this.handlers.right.attr("x"); + + var xl = this.handlers.left.attr('x'); + var xr = this.handlers.right.attr('x'); this.hideTipsy(); @@ -324,42 +326,42 @@ define([ this.ext.right = this.xscale(roundValue); this.domain - .attr("x1", this.ext.left + 16); + .attr('x1', this.ext.left + 16); // Move right handler this.handlers.right .transition() .duration(100) .ease('line') - .attr("x", this.xscale(roundValue) - 30); + .attr('x', this.xscale(roundValue) - 30); // Move domain right this.domain .transition() .duration(100) - .ease("line") - .attr("x2", this.xscale(roundValue) - 30); + .ease('line') + .attr('x2', this.xscale(roundValue) - 30); } else { if (this.ext.right < this.xscale(roundValue)) return; this.ext.left = this.xscale(roundValue); this.domain - .attr("x2", this.ext.right - 30); + .attr('x2', this.ext.right - 30); // Move left handler this.handlers.left .transition() .duration(100) .ease('line') - .attr("x", this.xscale(roundValue) + 16); + .attr('x', this.xscale(roundValue) + 16); // Move domain left this.domain .transition() .duration(100) - .ease("line") - .attr("x1", this.xscale(roundValue) + 16); + .ease('line') + .attr('x1', this.xscale(roundValue) + 16); } this.formatXaxis(); @@ -368,14 +370,14 @@ define([ formatXaxis: function() { var self = this; - d3.select(".xaxis").selectAll("text").filter(function(d, i) { + d3.select('.xaxis').selectAll('text').filter(function(d, i) { var left = self.ext.left + 16; var right = self.ext.right + 30; if (d > Math.round(self.xscale.invert(left)) && d < Math.round(self.xscale.invert(right))) { - d3.select(this).classed("selected", true); + d3.select(this).classed('selected', true); } else { - d3.select(this).classed("selected", false); + d3.select(this).classed('selected', false); } }); }, @@ -385,9 +387,9 @@ define([ * Update the timeline date. (calls updateTimelineDate) */ onBrushEnd: function(event) { - var startYear = Math.round(this.xscale.invert(this.handlers.left.attr("x"))); - var endYear = Math.round(this.xscale.invert(this.handlers.right.attr("x"))); - + var startYear = Math.round(this.xscale.invert(this.handlers.left.attr('x'))); + var endYear = Math.round(this.xscale.invert(this.handlers.right.attr('x'))); + setTimeout(function() { this.updateTimelineDate([moment([startYear]), moment([endYear])]); }.bind(this), 100); @@ -408,16 +410,16 @@ define([ }, showTipsy: function() { - this.tipsy.style("visibility", "visible"); - this.tooltip.style("visibility", "visible"); + this.tipsy.style('visibility', 'visible'); + this.tooltip.style('visibility', 'visible'); }, hideTipsy: function() { - this.tipsy.style("visibility", "hidden"); - this.tooltip.style("visibility", "hidden"); + this.tipsy.style('visibility', 'hidden'); + this.tooltip.style('visibility', 'hidden'); } }); return Timeline; -}); \ No newline at end of file +}); diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 2fcf409aad..c91529923c 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -35,4 +35,4 @@ def reset! Capybara.run_server = false Capybara.app_host = 'http://localhost:5000' -Capybara.default_driver = :selenium +Capybara.default_driver = :rack_test From 901bcb466d55e8ed43571a748bb77286957fb915 Mon Sep 17 00:00:00 2001 From: David Inga Date: Thu, 26 Jun 2014 18:17:38 +0200 Subject: [PATCH 178/823] grunt --- Gruntfile.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Gruntfile.js b/Gruntfile.js index 729b34ec27..59acfe86a9 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -44,8 +44,7 @@ module.exports = function(grunt) { grunt.registerTask('default', [ 'jshint', - 'testem', - 'watch' + 'testem' ]); }; From 76f853ef105112fa3f65deac0bc812f0b3905ed6 Mon Sep 17 00:00:00 2001 From: Pedro Garcia Date: Thu, 26 Jun 2014 19:12:25 +0200 Subject: [PATCH 179/823] deleting app.js collections/layers.js --- app/assets/javascripts/map/app.js | 21 ------ .../javascripts/map/collections/layers.js | 56 -------------- .../map/views/layers/core/CartodbLayerView.js | 74 +++++++++++++++++++ 3 files changed, 74 insertions(+), 77 deletions(-) delete mode 100644 app/assets/javascripts/map/app.js delete mode 100644 app/assets/javascripts/map/collections/layers.js create mode 100644 app/assets/javascripts/map/views/layers/core/CartodbLayerView.js diff --git a/app/assets/javascripts/map/app.js b/app/assets/javascripts/map/app.js deleted file mode 100644 index 166798aec7..0000000000 --- a/app/assets/javascripts/map/app.js +++ /dev/null @@ -1,21 +0,0 @@ -/** - * The application module. - * - * @return singleton instance of App class. - */ -define([ - 'Class' -], function (Class) { - - var App = Class.extend({ - init: function() { - console.log('App.initialize()'); - } - }); - - var app = new App(); - - - - return app; -}); \ No newline at end of file diff --git a/app/assets/javascripts/map/collections/layers.js b/app/assets/javascripts/map/collections/layers.js deleted file mode 100644 index b5258dddb0..0000000000 --- a/app/assets/javascripts/map/collections/layers.js +++ /dev/null @@ -1,56 +0,0 @@ -/** - * The layer collection module. - * - * @return singleton instance of LayerCollection class (extends - * Backbone.CartoDB.CartoDBCollection). - */ -define([ - 'jquery', - 'underscore', - 'backbone', - 'backbone_cartodb', - 'mps', - 'gmap', - 'presenter' -], function ($, _, Backbone, BackboneCartoDB, mps, gmap, presenter) { - - var LayerCollection = Backbone.CartoDB({user: 'wri-01'}).CartoDBCollection.extend({ - - sql: function() { - return ['SELECT cartodb_id AS id, slug, title, title_color, subtitle, sublayer, table_name, source, category_color, category_slug, category_name, external, zmin, zmax, ST_XMAX(the_geom) AS xmax,', - 'ST_XMIN(the_geom) AS xmin, ST_YMAX(the_geom) AS ymax, ST_YMIN(the_geom) AS ymin, tileurl, true AS visible', - 'FROM layerinfo_dev_copy', - 'WHERE display = TRUE ORDER BY displaylayer, title ASC'].join(' '); - }, - - getBaselayers: function() { - return _.where(this.toJSON(), {category_name: 'Forest change'}) - }, - - getBaselayer: function(layerName) { - var layer = _.where(this.toJSON(), {category_name: 'Forest change', - slug: layerName}); - if (layer) { - return layer[0]; - } - }, - - getSublayers: function() { - return _.filter(this.toJSON(), function(layer) { - return layer.category_name != 'Forest change'; - }); - }, - - getSublayer: function(layerName) { - var layer = _.where(this.getSublayers(), {slug: layerName}); - if (layer) { - return layer[0]; - } - } - }); - - var layerCollection = new LayerCollection(); - - return layerCollection; - -}); \ No newline at end of file diff --git a/app/assets/javascripts/map/views/layers/core/CartodbLayerView.js b/app/assets/javascripts/map/views/layers/core/CartodbLayerView.js new file mode 100644 index 0000000000..9c3e75b92c --- /dev/null +++ b/app/assets/javascripts/map/views/layers/core/CartodbLayerView.js @@ -0,0 +1,74 @@ +/** + * The Cartodb map layer module. + * + * @return CartodbLayer class (extends Backbone.View). + */ +define([ + 'backbone', + 'mps', + 'presenter', + 'wax' +], function(Backbone, mps, presenter, wax) { + + var CartodbLayerView = Backbone.View.extend({ + initialize: function() { + this.layer = {}; + this.layerOrder = this.layerOrder || 1; + this.rendered = false; + }, + + render: function() { + if (this.rendered) return; + + this.layer = new CartoDBLayer({ + // map: map.map, + user_name: '', + tiler_domain: this.url, + sql_domain: this.url, + extra_params: { v: this.global_version}, + tiler_path: '/tiles/', + tiler_suffix: '.png', + tiler_grid: '.grid.json', + table_name: this.table, + query: this.getQuery(), + layer_order: this.layerOrder, + opacity: 1, + interactivity: "cartodb_id", + debug: false, + auto_bound: false + }); + + this.rendered = true; + }, + + updateTiles: function() { + this.layer.setQuery(this.getQuery()); + }, + + getQuery: function() { + var timelineDate = presenter.get('timelineDate') || this.timeline.opts.dateRange; + + var sql = "SELECT * FROM " + + this.table + + " WHERE date between '" + + timelineDate[0].year() + + "-" + + //timelineDate[0].month() + + "1-1' AND '" + + timelineDate[1].year() + + "-" + + //timelineDate[1].month() + + "1-1'"; + + return sql; + }, + + removeLayer: function() { + mps.publish('map/remove-layer', [this.name]); + this.rendered = false; + } + + }); + + return CartodbLayerView; +}); \ No newline at end of file From 375f25e52cd23fc12d1c46443001e45814bbd937 Mon Sep 17 00:00:00 2001 From: Pedro Garcia Date: Thu, 26 Jun 2014 19:12:49 +0200 Subject: [PATCH 180/823] deleting app.js collections/layers.js --- .../map/views/layers/core/CartodbLayerView.js | 74 ------------------- 1 file changed, 74 deletions(-) delete mode 100644 app/assets/javascripts/map/views/layers/core/CartodbLayerView.js diff --git a/app/assets/javascripts/map/views/layers/core/CartodbLayerView.js b/app/assets/javascripts/map/views/layers/core/CartodbLayerView.js deleted file mode 100644 index 9c3e75b92c..0000000000 --- a/app/assets/javascripts/map/views/layers/core/CartodbLayerView.js +++ /dev/null @@ -1,74 +0,0 @@ -/** - * The Cartodb map layer module. - * - * @return CartodbLayer class (extends Backbone.View). - */ -define([ - 'backbone', - 'mps', - 'presenter', - 'wax' -], function(Backbone, mps, presenter, wax) { - - var CartodbLayerView = Backbone.View.extend({ - initialize: function() { - this.layer = {}; - this.layerOrder = this.layerOrder || 1; - this.rendered = false; - }, - - render: function() { - if (this.rendered) return; - - this.layer = new CartoDBLayer({ - // map: map.map, - user_name: '', - tiler_domain: this.url, - sql_domain: this.url, - extra_params: { v: this.global_version}, - tiler_path: '/tiles/', - tiler_suffix: '.png', - tiler_grid: '.grid.json', - table_name: this.table, - query: this.getQuery(), - layer_order: this.layerOrder, - opacity: 1, - interactivity: "cartodb_id", - debug: false, - auto_bound: false - }); - - this.rendered = true; - }, - - updateTiles: function() { - this.layer.setQuery(this.getQuery()); - }, - - getQuery: function() { - var timelineDate = presenter.get('timelineDate') || this.timeline.opts.dateRange; - - var sql = "SELECT * FROM " + - this.table + - " WHERE date between '" + - timelineDate[0].year() + - "-" + - //timelineDate[0].month() + - "1-1' AND '" + - timelineDate[1].year() + - "-" + - //timelineDate[1].month() + - "1-1'"; - - return sql; - }, - - removeLayer: function() { - mps.publish('map/remove-layer', [this.name]); - this.rendered = false; - } - - }); - - return CartodbLayerView; -}); \ No newline at end of file From d4bee32e129430d217cd75ba9267a77d4b1e9ea6 Mon Sep 17 00:00:00 2001 From: Pedro Garcia Date: Thu, 26 Jun 2014 19:14:28 +0200 Subject: [PATCH 181/823] deleting map.js --- app/assets/javascripts/map/views/map.js | 127 ------------------------ 1 file changed, 127 deletions(-) delete mode 100644 app/assets/javascripts/map/views/map.js diff --git a/app/assets/javascripts/map/views/map.js b/app/assets/javascripts/map/views/map.js deleted file mode 100644 index 46f73270e7..0000000000 --- a/app/assets/javascripts/map/views/map.js +++ /dev/null @@ -1,127 +0,0 @@ -/** - * The map module. - * - * View for the Google Map. - * - * @return singleton instance of Map class (extends Backbone.View). - */ -define([ - 'backbone', - 'underscore', - 'presenter', - 'mps', - 'views/searchbox', - 'views/maptypeSelector', - 'views/AnalysisButtonView' -], function(Backbone, _, presenter, mps, searchbox, maptypeSelector, - AnalysisButtonView) { - - var Map = Backbone.View.extend({ - - el: '#map', - - initialize: function() { - _.bindAll(this, 'onZoomChange', 'onCenterChange', 'updateZoom', 'updateCenter'); - var self = this; - - // Subscribe to add layer events - mps.subscribe('map/add-layer', function(layer) { - self.addLayer(layer); - }); - - // Subscribe to remove layer events - mps.subscribe('map/remove-layer', function(layer) { - self.removeLayer(layer); - }); - - // Subscribe to remove layer events - mps.subscribe('map/fit-bounds', function(bounds) { - self.fitBounds(bounds); - }); - }, - - render: function() { - var mapOptions = { - minZoom: 3, - }; - - this.map = new google.maps.Map(this.el, mapOptions); - this.resize(); - - // Listeners - google.maps.event.addListener(this.map, 'zoom_changed', this.onZoomChange); - google.maps.event.addListener(this.map, 'dragend', this.onCenterChange); - - // Add analysis button to map - this.$el.append(new AnalysisButtonView().$el); - }, - - /** - * Add supplied layer to map. - */ - addLayer: function(layer) { - this.map.overlayMapTypes.insertAt(0, layer); - }, - - /** - * Remove layer from map using supplied layer name. - */ - removeLayer: function(name) { - var overlays_length = this.map.overlayMapTypes.getLength(); - if (overlays_length > 0) { - for (var i = 0; i< overlays_length; i++) { - var layer = this.map.overlayMapTypes.getAt(i); - if (layer && layer.name == name) this.map.overlayMapTypes.removeAt(i); - } - } - }, - - /** - * Update map zoom - */ - updateZoom: function(zoom) { - this.map.setZoom(zoom); - }, - - /** - * Update map center - */ - updateCenter: function(latLngArr) { - var center = new google.maps.LatLng(latLngArr[0], latLngArr[1]); - this.map.setCenter(center); - }, - - /** - * Update map type - */ - updateMapType: function(maptype) { - this.map.setMapTypeId(maptype); - }, - - onZoomChange: function() { - presenter.set('zoom', this.map.zoom, {silent: true}); - presenter.updateUrl(); - }, - - onCenterChange: function() { - var center = this.map.getCenter(); - presenter.set('latLng', [center.lat(), center.lng()], {silent: true}); - presenter.updateUrl(); - }, - - resize: function() { - google.maps.event.trigger(this.map, 'resize'); - this.map.setZoom(this.map.getZoom()); - this.map.setCenter(this.map.getCenter()); - }, - - fitBounds: function(bounds) { - this.map.fitBounds(bounds); - } - }); - - var map = new Map(); - - return map; - -}); \ No newline at end of file From feafb4b94fe0e32fc0a748121cfc9aeb6c114e7d Mon Sep 17 00:00:00 2001 From: Aaron Date: Thu, 26 Jun 2014 14:56:48 -0700 Subject: [PATCH 182/823] wip: refactor PlaceService with tests --- .../map/presenters/MapPresenter.js | 54 ++-- app/assets/javascripts/map/router.js | 43 ++- .../javascripts/map/services/PlaceService.js | 272 +++++++++++++----- app/assets/javascripts/map/views/MapView.js | 35 ++- .../map/views/layers/UMDLossLayerView.js | 10 +- jstest/spec/MapPresenter_spec.js | 50 +--- jstest/spec/PlaceService_spec.js | 71 +++-- 7 files changed, 339 insertions(+), 196 deletions(-) diff --git a/app/assets/javascripts/map/presenters/MapPresenter.js b/app/assets/javascripts/map/presenters/MapPresenter.js index 34cd769ae0..656139d0af 100644 --- a/app/assets/javascripts/map/presenters/MapPresenter.js +++ b/app/assets/javascripts/map/presenters/MapPresenter.js @@ -34,11 +34,14 @@ define([ }, this)); mps.subscribe('Place/go', _.bind(function(place) { - if (place.getName() === 'map') { - this._initMap(place); - this._initLayers(place); + if (place.name === 'map') { + this.layers = place.params.layers; + this._initMap(place.params); + this._initLayers(this.layers); } - }, this)); + }, this)); + + mps.publish('Place/register', [this]); }, /** @@ -46,14 +49,9 @@ define([ * * @param {PlaceService} The place to go to */ - _initMap: function(place) { - var zoom = place.getMapZoom(); - var center = place.getMapCenter(); - var mapTypeId = place.getMapTypeId(); - - this.view.setZoom(zoom); - this.view.setCenter(center.lat, center.lng); - this.view.setMapTypeId(mapTypeId); + _initMap: function(params) { + this.view.initMap(params); + mps.publish('Map/initialized', []); }, /** @@ -61,11 +59,31 @@ define([ * * @param {PlaceService} The place to go to */ - _initLayers: function(place) { - place.getMapLayers(_.bind(function(layers) { - this.view.initLayers(layers); - mps.publish('Map/layers-initialized', []); - }, this)); + _initLayers: function(layers) { + this.view.initLayers(layers); + mps.publish('Map/layers-initialized', []); + }, + + getPlaceParams: function() { + var params = {}; + var mapCenter = this.view.getCenter(); + var baseLayers = _.where(this.layers, {category_slug: 'forest_clearing'}); + var subLayers = _.filter(this.layers, function(layer) { + return layer.category_slug !== 'forest_clearing'; + }); + + params.zoom = this.view.getZoom(); + params.lat = mapCenter.lat; + params.lng = mapCenter.lng; + params.maptype = this.view.getMapTypeId(); + params.baselayers = _.map(baseLayers, function(layer) { + return layer.slug; + }); + params.sublayers = _.map(subLayers, function(layer) { + return layer.id; + }); + + return params; }, /** @@ -76,6 +94,7 @@ define([ */ onZoomChange: function(zoom) { mps.publish('Map/zoom-change', [zoom]); + mps.publish('Place/update', [{go: false}]); }, /** @@ -87,6 +106,7 @@ define([ */ onCenterChange: function(lat, lng) { mps.publish('Map/center-change', [lat, lng]); + mps.publish('Place/update', [{go: false}]); } }); diff --git a/app/assets/javascripts/map/router.js b/app/assets/javascripts/map/router.js index f2e67778c3..1a69b0be29 100644 --- a/app/assets/javascripts/map/router.js +++ b/app/assets/javascripts/map/router.js @@ -2,7 +2,7 @@ * The router module. * * Router handles app routing and URL parameters and updates Presenter. - * + * * @return singleton instance of Router class (extends Backbone.Router). */ define([ @@ -11,16 +11,13 @@ define([ 'backbone', 'mps', 'gmap', - 'presenter', - 'collections/layers', 'views/MapView', 'services/PlaceService', 'services/MapLayerService' -], function($, _, Backbone, mps, gmap, presenter, layersCollection, MapView, - PlaceService, mapLayerService) { +], function($, _, Backbone, mps, gmap, MapView, PlaceService, mapLayerService) { 'use strict'; - + var Router = Backbone.Router.extend({ routes: { @@ -29,9 +26,13 @@ define([ }, initialize: function() { - _.bindAll(this, 'navigateTo'); - mps.subscribe('navigate', this.navigateTo); + mps.subscribe('Place/go', _.bind(function(place) { + if (place.route) { + this.navigate('map/' + this.route, {silent: true}); + } + }, this)); this.setMapSize(); + this.placeService = new PlaceService(mapLayerService, this); }, map: function(zoom, lat, lng, iso, maptype, baselayers, sublayers) { @@ -46,34 +47,22 @@ define([ }; var queryParams = _.parseUrl(); var params = _.extend(pathParams, queryParams); - var place = new PlaceService('map', params, mapLayerService); - + gmap.init(_.bind(function() { if (!this.mapView) { this.mapView = new MapView(); - this.mapView.render(); } - mps.publish('Place/go', [place]); + mps.publish( + 'Place/update', [{go: true, name: 'map', params: params}]); }, this)); }, - navigateTo: function(place) { - this.path = place.path; - delete place.path; - this.navigate('map/' + this.path, place); - }, - setMapSize: function() { - var dh = $(window).height(), - $map = $('#map'); + var dh = $(window).height(), + $map = $('#map'); $map.height(dh - 69); - - $('.header-nav__logo').css({ - position: 'absolute', - top: 69 - }); - + $('.header-nav__logo').css({ position: 'absolute', top: 69 }) setTimeout(function() { $('html, body').scrollTop(69); }, 500); @@ -85,4 +74,4 @@ define([ return router; -}); +}); \ No newline at end of file diff --git a/app/assets/javascripts/map/services/PlaceService.js b/app/assets/javascripts/map/services/PlaceService.js index 1179d55938..1b4b1eda05 100644 --- a/app/assets/javascripts/map/services/PlaceService.js +++ b/app/assets/javascripts/map/services/PlaceService.js @@ -1,115 +1,237 @@ /** - * Service for accessing information about a place. + * The PlaceService class manages places in the application. * - * A place contains all the state needed to render views. For example, it - * contains the map zoom, center coordinates, maptype, layers. etc. + * A place is just the current state of the application which can be + * represented as an Object or a URL. For example, the place associated with: + * + * http://localhost:5000/map/6/2/17/ALL/terrain/loss + * + * Can also be represented like this: + * + * zoom - 6 + * lat - 2 + * lng - 17 + * iso - ALL + * maptype - terrain + * baselayers - loss + * + * The PlaceService class handles the following use cases: + * + * 1) New route updates views + * + * The Router receives a new URL and all application views need to be updated + * with the state encoded in the URL. + * + * Here the router publishes the "Place/update" event passing in the route + * name and route parameters. The PlaceService handles the event by + * standardizing the route parameters and publishing them in a "Place/go" + * event. Any presenters listening to the event receives the updated + * application state and can update their views. + * + * 2) Updated view updates URL + * + * A View state changes (e.g., a new map zoom) and the URL needs to be + * updated, not only with its new state, but from the stae of all views in + * the application that provide state for URLs. + * + * Here presenters publishe the "Place/register" event passing in a + * reference to themselves. The PlaceService subscribes to the + * "Place/register" event so that it can keep references to all presenters + * that provide state. Then the view publishes the "Place/update" event + * passing in a "go" parameter. If "go" is false, the PlaceService will + * update the URL. Otherwise it will publish the "Place/go" event which will + * notify all subscribed presenters. + * + * @return {PlaceService} The PlaceService class */ define([ 'Class', + 'mps', + 'uri', 'underscore' -], function (Class, _) { +], function (Class, mps, UriTemplate, _) { 'use strict'; var PlaceService = Class.extend({ - init: function(name, params, mapLayerService) { - this.name = name; - this.params = params; + /*jshint multistr: true */ + _uriTemplate: 'map{/zoom}{/lat}{/lng}{/iso}{/maptype}{/baselayers}{/sublayers}{?begin,end}', + + /** + * Create new PlaceService with supplied MapLayerService and + * Backbone.Router. + * + * @param {MapLayerService} mapLayerService Instance of MapLayerService + * @param {Backbond.Router} router Instance of Backbone.Router + * @return {PlaceService} Instance of PlaceService + */ + init: function(mapLayerService, router) { this.mapLayerService = mapLayerService; + this.router = router; + this._presenters = []; + this._subscribe(); }, - _toNumber: function(val) { - var num = null; + /** + * Subscribe to application events. + */ + _subscribe: function() { + mps.subscribe('Place/register', _.bind(function(presenter) { + this._presenters = _.union(this._presenters, [presenter]); + }, this)); + + mps.subscribe('Place/update', _.bind(function(place) { + this._handleNewPlace(place.name, place.params, place.go); + }, this)); + }, - try { - num = Number(val); - if (isNaN(num)) { - throw 'Not a number'; - } - return num; - } catch(err) { - return undefined; + /** + * Handles a new place. + * + * If go is true, fires a Place/go event passing in the place parameters + * which will include layers retrieved from the MapLayerService. Otherwise + * the URL is silently updated with a new route. + * + * @param {string} name The place name + * @param {Object} params The place parameters + * @param {[type]} go True to publish Place/go event, false to update URL + */ + _handleNewPlace: function(name, params, go) { + var route = null; + var newPlace = {}; + + if (!params) { + params = this._getPresenterParams(this._presenters); } - }, - getName: function() { - return this.name; + newPlace.params = this._standardizeParams(params); + newPlace.name = name; + + if (go) { + this._getMapLayers( + params.baselayers, + params.sublayers, + _.bind(function(layers) { + newPlace.params.layers = layers; + mps.publish('Place/go', [newPlace]); + }, this)); + } else { + route = this._getRoute(newPlace.params); + this.router.navigate(route, {silent: true}); + } }, - getBeginDate: function() { - return this._toNumber(this.params.begin); + _getRoute: function(params) { + return new UriTemplate(this._uriTemplate).fillFromObject(params); }, - getEndDate: function() { - return this._toNumber(this.params.end); + /** + * Return standardized representation of supplied params object. + * + * @param {Object} params The params to standardize + * @return {Object} The standardized params. + */ + _standardizeParams: function(params) { + var p = _.clone(params); + p.zoom = this._toNumber(params.zoom); + p.lat = this._toNumber(params.lat); + p.lng = this._toNumber(params.lng); + p.maptype = params.maptype; + p.begin = this._toNumber(params.begin); + p.end = this._toNumber(params.end); + p.iso = params.iso || 'ALL'; + return p; }, - getMapZoom: function() { - return this._toNumber(this.params.zoom); + /** + * Return param object representing state from all registered presenters + * that implement getPlaceParams(). + * + * @param {Array} presenters The registered presenters + * @return {Object} Params representing state from all presenters + */ + _getPresenterParams: function(presenters) { + var params = {}; + + _.each(presenters, _.bind(function(presenter) { + _.extend(params, presenter.getPlaceParams()); + }, this)); + + return params; }, - getMapCenter: function() { - var lat = this._toNumber(this.params.lat); - var lng = this._toNumber(this.params.lng); + + /** + * Returns supplied value as a Number or as undefined if it's not a number. + * + * @param {string} val The value to convert + * @return {Number|undefined} The converted Number or undefined + */ + _toNumber: function(val) { + var num = null; - if (lat && lng) { - return {lat: lat, lng: lng}; - } else { + try { + if (val === undefined || val === null || val.trim() === '') { + throw "Not a number"; + } + num = Number(val); + if (isNaN(num)) { + throw "Not a number"; + } + return num; + } catch(err) { return undefined; } }, - getIso: function() { - return this.params.iso; - }, - - getMapTypeId: function() { - return this.params.maptype; - }, - - getMapLayers: function(callback) { - var params = this.params; - var baselayers = null; - var baseWhere = null; - var sublayers = null; - var subWhere = null; - var where = null; - - // Return immediately if mapLayers are available - if (this.mapLayers) { - callback(this.mapLayers); - return; - } - - params = this.params; - - // Create base layer filters - baselayers = params.baselayers ? params.baselayers.split(',') : []; - baseWhere = _.map( - baselayers, - function (name) { - return {slug: name, category_slug: 'forest_clearing'}; + /** + * Return array of filter objects {slug:, category_slug:} for baselayers. + * + * @param {string} layers CSV list of baselayer slug names + * @return {Array} Filter objects for baselayers + */ + _getBaselayerFilters: function(layers) { + var baselayers = layers ? layers.split(',') : []; + var filters = _.map(baselayers, function (name) { + return {slug: name, category_slug: 'forest_clearing'}; }); + + return filters; + }, - // Create sublayer filters - sublayers = params.sublayers ? params.sublayers.split(',') : []; - subWhere = _.map( - sublayers, - function(id) { - return {id: id}; + /** + * Return array of filter objects {id:} for sublayers. + * + * @param {string} layers CSV list of sublayer ids + * @return {Array} Filter objects for sublayers + */ + _getSublayerFilters: function(layers) { + var sublayers = layers ? layers.split(',') : []; + var filters = _.map(sublayers, function(id) { + return {id: id}; }); + + return filters; + }, - // Combine layer filters with order preserved - where = _.union(baseWhere, subWhere); + /** + * Asynchronously get map layers objects from MapLayerService. + * + * @param {string} baselayers CSV list of baselayer slug names + * @param {string} sublayers CSV list of sublayer ids + * @param {Function} callback Called with layers + */ + _getMapLayers: function(baselayers, sublayers, callback) { + var baseWhere = this._getBaselayerFilters(baselayers); + var subWhere = this._getSublayerFilters(sublayers); + var where = _.union(baseWhere, subWhere); // Preserves order // Get layers from MapLayerService this.mapLayerService.getLayers( where, _.bind(function(layers) { - console.log(layers); - this.mapLayers = layers; - callback(this.mapLayers); + callback(layers); }, this), _.bind(function(error) { console.error(error); @@ -117,6 +239,6 @@ define([ }, this)); } }); - + return PlaceService; -}); +}); \ No newline at end of file diff --git a/app/assets/javascripts/map/views/MapView.js b/app/assets/javascripts/map/views/MapView.js index f8e020c2ae..375f045286 100644 --- a/app/assets/javascripts/map/views/MapView.js +++ b/app/assets/javascripts/map/views/MapView.js @@ -28,14 +28,7 @@ define([ /** * Creates the Google Maps and attaches it to the DOM. */ - render: function() { - var options = { - minZoom: 3, - zoom: 3, - mapTypeId: google.maps.MapTypeId.TERRAIN, - center: new google.maps.LatLng(15, 27) - }; - + render: function(options) { this.map = new google.maps.Map(this.el, options); this.resize(); this._addCompositeViews(); @@ -65,6 +58,16 @@ define([ }, this)); }, + initMap: function(params) { + var options = { + minZoom: 3, + zoom: params.zoom, + mapTypeId: params.maptype, + center: new google.maps.LatLng(params.lat, params.lng) + }; + this.render(options); + }, + /** * Used by MapPresenter to initialize the map view. This function clears * all layers from the map and then adds supplied layers in order. @@ -103,7 +106,7 @@ define([ if (layer.slug === 'loss') { if (!_.has(this.layerViews, 'loss')) { - layerView = new UMDLossLayerView(); + layerView = new UMDLossLayerView(layer); this.layerViews.loss = layerView; } } @@ -119,6 +122,10 @@ define([ this.map.setZoom(zoom); }, + getZoom: function() { + return this.map.getZoom(); + }, + /** * Used by MapPresenter to set the map center. * @@ -129,6 +136,12 @@ define([ this.map.setCenter(new google.maps.LatLng(lat, lng)); }, + getCenter: function() { + var center = this.map.getCenter(); + + return {lat: center.lat(), lng: center.lng()}; + }, + /** * Used by MapPresenter to set the map type. * @@ -138,6 +151,10 @@ define([ this.map.setMapTypeId(maptype); }, + getMapTypeId: function() { + return this.map.getMapTypeId(); + }, + /** * Handles a map zoom change UI event by dispatching to MapPresenter. */ diff --git a/app/assets/javascripts/map/views/layers/UMDLossLayerView.js b/app/assets/javascripts/map/views/layers/UMDLossLayerView.js index d41cf0f789..1593f07736 100644 --- a/app/assets/javascripts/map/views/layers/UMDLossLayerView.js +++ b/app/assets/javascripts/map/views/layers/UMDLossLayerView.js @@ -67,11 +67,19 @@ define([ this.timelineDate = timelineDate; }, + getLayer: function() { + return this.layer; + }, + /** * Return the view name */ getName: function() { - return this.name; + return this.layer.name; + }, + + getCategory: function() { + return this.layer.category_slug; } }); diff --git a/jstest/spec/MapPresenter_spec.js b/jstest/spec/MapPresenter_spec.js index 4e74e0efaa..2e73ada301 100644 --- a/jstest/spec/MapPresenter_spec.js +++ b/jstest/spec/MapPresenter_spec.js @@ -27,58 +27,26 @@ define([ zoom: 8, maptype: 'terrain', lat: 1, - lng: 2 + lng: 2, + layers: ['layers'] } }; - beforeEach(function(done) { - jasmine.Ajax.install(); - nsa.test = true; + beforeEach(function() { viewSpy = jasmine.createSpyObj( 'viewSpy', - ['initLayers', 'setZoom', 'setCenter', 'setMapTypeId']); - - placeService = jasmine.createSpyObj( - 'placeService', - ['getBeginDate', 'getEndDate', 'getMapZoom', 'getMapCenter', 'getIso', - 'getMapTypeId', 'getMapLayers', 'getName']); - placeService.getName.and.returnValue('map'); - placeService.getMapZoom.and.returnValue(8); - placeService.getMapCenter.and.returnValue({lat: 1, lng: 2}); - placeService.getMapTypeId.and.returnValue('terrain'); - placeService.getMapLayers = function(callback) { - callback('layers'); - done(); - }; - spyOn(placeService, 'getMapLayers').and.callThrough(); - + ['initLayers', 'initMap']); presenter = new MapPresenter(viewSpy); - mps.publish('Place/go', [placeService]); - request = jasmine.Ajax.requests.mostRecent(); - request.response(ApiResponse.layers.success); + mps.publish('Place/go', [place]); }); it("Check Place/go handling", function() { - var layers = 'layers'; + expect(viewSpy.initMap).toHaveBeenCalled(); + expect(viewSpy.initMap).toHaveBeenCalledWith(place.params); + expect(viewSpy.initMap.calls.count()).toEqual(1); - // Zoom - expect(viewSpy.setZoom).toHaveBeenCalled(); - expect(viewSpy.setZoom).toHaveBeenCalledWith(8); - expect(viewSpy.setZoom.calls.count()).toEqual(1); - - // Center - expect(viewSpy.setCenter).toHaveBeenCalled(); - expect(viewSpy.setCenter).toHaveBeenCalledWith(1, 2); - expect(viewSpy.setCenter.calls.count()).toEqual(1); - - // Maptype - expect(viewSpy.setMapTypeId).toHaveBeenCalled(); - expect(viewSpy.setMapTypeId).toHaveBeenCalledWith('terrain'); - expect(viewSpy.setMapTypeId.calls.count()).toEqual(1); - - // TODO check initLayers expect(viewSpy.initLayers).toHaveBeenCalled(); - expect(viewSpy.initLayers).toHaveBeenCalledWith(layers); + expect(viewSpy.initLayers).toHaveBeenCalledWith(place.params.layers); expect(viewSpy.initLayers.calls.count()).toEqual(1); }); diff --git a/jstest/spec/PlaceService_spec.js b/jstest/spec/PlaceService_spec.js index 4969d95af3..589881a13e 100644 --- a/jstest/spec/PlaceService_spec.js +++ b/jstest/spec/PlaceService_spec.js @@ -2,8 +2,10 @@ * Unit test coverage for PlaceService. */ define([ - 'services/PlaceService' -], function(PlaceService) { + 'services/PlaceService', + 'mps', + 'nsa' +], function(PlaceService, mps, nsa ) { describe("The PlaceService", function() { var params = null; @@ -12,52 +14,69 @@ define([ beforeEach(function() { params = { - zoom: 8, - lat: 1.1, - lng: 2, + zoom: '8', + lat: '1.1', + lng: '2', iso: 'idn', maptype: 'terrain', baselayers: 'loss', sublayers: '1,2,3', - begin: 2014, - end: 3014 + begin: '2014', + end: '3014' }; }); describe("Test place getter functions", function() { // Mock MapLayerService var mapLayerService = null; + var router = null; var layersResult = null; - var callback = null; + var placeResult = null; - beforeEach(function() { + beforeEach(function(done) { + jasmine.Ajax.install(); + nsa.test = true; // The mock MapLayerService mapLayerService = { getLayers: function(where, successCb, errorCb) { - callback('layers'); + successCb('layers'); } }; spyOn(mapLayerService, 'getLayers').and.callThrough(); - - // Callback for getMapLayers() - callback = function(layers) { - layersResult = layers; + + router = { + navigate: function() { + } }; + spyOn(router, 'navigate'); + + mps.subscribe('Place/go', function(place) { + placeResult = place; + done(); + }); + + // The PlaceService with mocked in MapLayerService and router + service = new PlaceService(mapLayerService, router); + + mps.publish("Place/update", [{name: 'map', params: params}]); - // The PlaceService with mocked in MapLayerService - service = new PlaceService('map', params, mapLayerService); }); - it("Place values are correct", function() { - service.getMapLayers(callback); - expect(mapLayerService.getLayers).toHaveBeenCalled(); - expect(layersResult).toEqual('layers'); - expect(service.getMapZoom()).toEqual(8); - expect(service.getMapCenter()).toEqual({lat: 1.1, lng: 2}); - expect(service.getIso()).toEqual('idn'); - expect(service.getMapTypeId()).toEqual('terrain'); - expect(service.getBeginDate()).toEqual(2014); - expect(service.getEndDate()).toEqual(3014); + it("Place values are correct", function() { + + expect(placeResult).not.toBe(null); + expect(placeResult.params).not.toBe(null); + expect(placeResult.name).not.toBe(null); + expect(placeResult.params).toEqual(jasmine.objectContaining({ + layers: 'layers', + zoom: 8, + lat: 1.1, + lng: 2, + iso: 'idn', + maptype: 'terrain', + begin: 2014, + end: 3014 + })); }); }); }); From efa8dec7f93fb195d7fb81c65b77aee347e3ed54 Mon Sep 17 00:00:00 2001 From: Aaron Date: Thu, 26 Jun 2014 22:41:37 -0700 Subject: [PATCH 183/823] wip: refactor PlaceService with tests --- jstest/spec/PlaceService_spec.js | 223 +++++++++++++++++++++++++++---- 1 file changed, 194 insertions(+), 29 deletions(-) diff --git a/jstest/spec/PlaceService_spec.js b/jstest/spec/PlaceService_spec.js index 589881a13e..ea0e199314 100644 --- a/jstest/spec/PlaceService_spec.js +++ b/jstest/spec/PlaceService_spec.js @@ -7,7 +7,7 @@ define([ 'nsa' ], function(PlaceService, mps, nsa ) { - describe("The PlaceService", function() { + describe("PlaceService Suite", function() { var params = null; var name = 'map'; var service = null; @@ -25,50 +25,185 @@ define([ end: '3014' }; }); - - describe("Test place getter functions", function() { - // Mock MapLayerService - var mapLayerService = null; - var router = null; - var layersResult = null; - var placeResult = null; - - beforeEach(function(done) { + + + /** + * Spec for testing _getPresenterParams(). + */ + describe("_getPresenterParams()", function() { + + beforeEach(function() { + service = new PlaceService({}, {}); + }); + + it("correctly gets params from 0 registered presenters", function() { + var resultParams = service._getPresenterParams([]); + + expect(resultParams).toEqual({}); + }); + + it("correctly gets params from 1 registered presenters", function() { + var presenter = jasmine.createSpyObj('presenter', ['getPlaceParams']); + var params = {boom: 'boom'}; + + presenter.getPlaceParams.and.returnValue(params); + expect(service._getPresenterParams([presenter])).toEqual(params); + }); + + it("correctly gets params from 2 registered presenters", function() { + var p1 = jasmine.createSpyObj('presenter', ['getPlaceParams']); + var p2 = jasmine.createSpyObj('presenter', ['getPlaceParams']); + var params1 = {boom: 'boom'}; + var params2 = {bam: 'bam'}; + var params = _.extend(params1, params2); + + p1.getPlaceParams.and.returnValue(params1); + p2.getPlaceParams.and.returnValue(params2); + expect(service._getPresenterParams([p1, p2])).toEqual(params); + }); + }); + + /** + * Spec for testing _getRoute(). + */ + describe("_getRoute()", function() { + + beforeEach(function() { + service = new PlaceService({}, {}); + }); + + it("correctly returns route", function() { + var r = 'map/8/1.1/2/idn/terrain/loss/1%2C2%2C3?begin=2014&end=3014'; + expect(service._getRoute(params)).toEqual(r); + }); + }); + + + /** + * Spec for testing _handleNewPlace(). + */ + describe("_handleNewPlace()", function() { + var mockLayerService = null; + var mockRouter = null; + + beforeEach(function() { + // Mock Ajax since it will call MapServiceLayer jasmine.Ajax.install(); nsa.test = true; - // The mock MapLayerService - mapLayerService = { + + // Mock MapServiceLayer and Router + mockLayerService = { getLayers: function(where, successCb, errorCb) { successCb('layers'); } }; - spyOn(mapLayerService, 'getLayers').and.callThrough(); - - router = { - navigate: function() { - } - }; - spyOn(router, 'navigate'); + spyOn(mockLayerService, 'getLayers').and.callThrough(); + mockRouter = jasmine.createSpyObj('router', ['navigate']); + service = new PlaceService(mockLayerService, mockRouter); + }); + + it("correctly publishes Place/go event when go is true", function(done) { mps.subscribe('Place/go', function(place) { - placeResult = place; + expect(place.params).toEqual(jasmine.objectContaining({ + zoom: 8, + lat: 1.1, + lng: 2, + iso: 'idn', + maptype: 'terrain', + begin: 2014, + end: 3014, + layers: 'layers' + })); done(); }); + service._handleNewPlace('map', params, true); + }); - // The PlaceService with mocked in MapLayerService and router - service = new PlaceService(mapLayerService, router); + it("correctly calls router.navigate when go is false", function() { + var r = 'map/8/1.1/2/idn/terrain/loss/1%2C2%2C3?begin=2014&end=3014'; - mps.publish("Place/update", [{name: 'map', params: params}]); + service._handleNewPlace('map', params, false); + expect(mockRouter.navigate).toHaveBeenCalledWith(r, {silent: true}); + }); + }); + + /** + * Spec for testing _toNumber(). + */ + describe("_toNumber()", function() { + + beforeEach(function() { + service = new PlaceService({}, {}); + }); + it("correctly returns numbers for numbers", function() { + expect(service._toNumber('1')).toEqual(1); + expect(service._toNumber('1.1')).toEqual(1.1); }); - it("Place values are correct", function() { + it("correctly returns undefined for non-numbers", function() { + expect(service._toNumber('a')).toEqual(undefined); + expect(service._toNumber('')).toEqual(undefined); + expect(service._toNumber(undefined)).toEqual(undefined); + expect(service._toNumber(null)).toEqual(undefined); + }); + }); + + + /** + * Spec for testing _getBaselayerFilters(). + */ + describe("_getBaselayerFilters()", function() { - expect(placeResult).not.toBe(null); - expect(placeResult.params).not.toBe(null); - expect(placeResult.name).not.toBe(null); - expect(placeResult.params).toEqual(jasmine.objectContaining({ - layers: 'layers', + beforeEach(function() { + service = new PlaceService({}, {}); + }); + + it("correctly returns filter for single layer", function() { + var f1 = {slug: '1', category_slug: 'forest_clearing'}; + var f2 = {slug: '2', category_slug: 'forest_clearing'}; + + expect(service._getBaselayerFilters('')).toEqual([]); + expect(service._getBaselayerFilters('1')).toEqual([f1]); + expect(service._getBaselayerFilters('1,2')).toEqual([f1, f2]); + }); + }); + + + /** + * Spec for testing _getSublayerFilters(). + */ + describe("_getSublayerFilters()", function() { + + beforeEach(function() { + service = new PlaceService({}, {}); + }); + + it("correctly returns filter for single layer", function() { + var f1 = {id: '1'}; + var f2 = {id: '2'}; + + expect(service._getSublayerFilters('')).toEqual([]); + expect(service._getSublayerFilters('1')).toEqual([f1]); + expect(service._getSublayerFilters('1,2')).toEqual([f1, f2]); + }); + }); + + + /** + * Spec for testing _standardizeParams(). + */ + describe("_standardizeParams()", function() { + + beforeEach(function() { + service = new PlaceService({}, {}); + }); + + it("correctly standardizes input parameters", function() { + var resultParams = service._standardizeParams(params); + + expect(resultParams).toEqual(jasmine.objectContaining({ zoom: 8, lat: 1.1, lng: 2, @@ -78,6 +213,36 @@ define([ end: 3014 })); }); + + it("correctly adds a default ALL iso parameter", function() { + var testParams = _.omit(params, 'iso'); + var resultParams = service._standardizeParams(testParams); + + expect(resultParams).toEqual(jasmine.objectContaining({ + zoom: 8, + lat: 1.1, + lng: 2, + iso: 'ALL', + maptype: 'terrain', + begin: 2014, + end: 3014 + })); + }); + }); + + + + describe("Test Place/register event", function() { + var presenter = {name: 'presenter'}; + + beforeEach(function() { + service = new PlaceService({}, {}); + mps.publish('Place/register', [presenter]); + }); + + it("Presenter registered", function() { + expect(service._presenters).toEqual([presenter]); + }); }); }); }); \ No newline at end of file From 8f7eeec28ebad790b694059fd78883c9a2a481a0 Mon Sep 17 00:00:00 2001 From: Aaron Date: Thu, 26 Jun 2014 23:06:28 -0700 Subject: [PATCH 184/823] add docs to MapPresenter --- app/assets/javascripts/map/presenters/MapPresenter.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/app/assets/javascripts/map/presenters/MapPresenter.js b/app/assets/javascripts/map/presenters/MapPresenter.js index 656139d0af..36cf88dcef 100644 --- a/app/assets/javascripts/map/presenters/MapPresenter.js +++ b/app/assets/javascripts/map/presenters/MapPresenter.js @@ -64,6 +64,12 @@ define([ mps.publish('Map/layers-initialized', []); }, + /** + * Retuns place parameters representing the state of the MapView and + * layers. Called by PlaceService. + * + * @return {Object} Params representing the state of the MapView and layers + */ getPlaceParams: function() { var params = {}; var mapCenter = this.view.getCenter(); From 17b9b8639274c53e08b9ed31e84ff7ec2a33f0b4 Mon Sep 17 00:00:00 2001 From: Aaron Date: Fri, 27 Jun 2014 00:40:35 -0700 Subject: [PATCH 185/823] bugfix: routes were not getting updated correctly --- .../javascripts/map/services/PlaceService.js | 17 ++----- jstest/spec/PlaceService_spec.js | 49 ++++++++++--------- 2 files changed, 31 insertions(+), 35 deletions(-) diff --git a/app/assets/javascripts/map/services/PlaceService.js b/app/assets/javascripts/map/services/PlaceService.js index 1b4b1eda05..31643bf90b 100644 --- a/app/assets/javascripts/map/services/PlaceService.js +++ b/app/assets/javascripts/map/services/PlaceService.js @@ -169,19 +169,12 @@ define([ * @return {Number|undefined} The converted Number or undefined */ _toNumber: function(val) { - var num = null; - - try { - if (val === undefined || val === null || val.trim() === '') { - throw "Not a number"; - } - num = Number(val); - if (isNaN(num)) { - throw "Not a number"; - } - return num; - } catch(err) { + if ((val === undefined || val === null || String(val).trim() === '')) { return undefined; + } else if (isNaN(val)) { + return undefined; + } else { + return Number(val); } }, diff --git a/jstest/spec/PlaceService_spec.js b/jstest/spec/PlaceService_spec.js index ea0e199314..d449110c38 100644 --- a/jstest/spec/PlaceService_spec.js +++ b/jstest/spec/PlaceService_spec.js @@ -5,9 +5,11 @@ define([ 'services/PlaceService', 'mps', 'nsa' -], function(PlaceService, mps, nsa ) { +], function(PlaceService, mps, nsa ) { - describe("PlaceService Suite", function() { + 'use strict'; + + describe('PlaceService Suite', function() { var params = null; var name = 'map'; var service = null; @@ -30,19 +32,19 @@ define([ /** * Spec for testing _getPresenterParams(). */ - describe("_getPresenterParams()", function() { + describe('_getPresenterParams()', function() { beforeEach(function() { service = new PlaceService({}, {}); }); - it("correctly gets params from 0 registered presenters", function() { + it('correctly gets params from 0 registered presenters', function() { var resultParams = service._getPresenterParams([]); expect(resultParams).toEqual({}); }); - it("correctly gets params from 1 registered presenters", function() { + it('correctly gets params from 1 registered presenters', function() { var presenter = jasmine.createSpyObj('presenter', ['getPlaceParams']); var params = {boom: 'boom'}; @@ -50,7 +52,7 @@ define([ expect(service._getPresenterParams([presenter])).toEqual(params); }); - it("correctly gets params from 2 registered presenters", function() { + it('correctly gets params from 2 registered presenters', function() { var p1 = jasmine.createSpyObj('presenter', ['getPlaceParams']); var p2 = jasmine.createSpyObj('presenter', ['getPlaceParams']); var params1 = {boom: 'boom'}; @@ -66,13 +68,13 @@ define([ /** * Spec for testing _getRoute(). */ - describe("_getRoute()", function() { + describe('_getRoute()', function() { beforeEach(function() { service = new PlaceService({}, {}); }); - it("correctly returns route", function() { + it('correctly returns route', function() { var r = 'map/8/1.1/2/idn/terrain/loss/1%2C2%2C3?begin=2014&end=3014'; expect(service._getRoute(params)).toEqual(r); }); @@ -82,7 +84,7 @@ define([ /** * Spec for testing _handleNewPlace(). */ - describe("_handleNewPlace()", function() { + describe('_handleNewPlace()', function() { var mockLayerService = null; var mockRouter = null; @@ -103,7 +105,7 @@ define([ service = new PlaceService(mockLayerService, mockRouter); }); - it("correctly publishes Place/go event when go is true", function(done) { + it('correctly publishes Place/go event when go is true', function(done) { mps.subscribe('Place/go', function(place) { expect(place.params).toEqual(jasmine.objectContaining({ zoom: 8, @@ -120,7 +122,7 @@ define([ service._handleNewPlace('map', params, true); }); - it("correctly calls router.navigate when go is false", function() { + it('correctly calls router.navigate when go is false', function() { var r = 'map/8/1.1/2/idn/terrain/loss/1%2C2%2C3?begin=2014&end=3014'; service._handleNewPlace('map', params, false); @@ -131,18 +133,19 @@ define([ /** * Spec for testing _toNumber(). */ - describe("_toNumber()", function() { + describe('_toNumber()', function() { beforeEach(function() { service = new PlaceService({}, {}); }); - it("correctly returns numbers for numbers", function() { + it('correctly returns numbers for numbers', function() { expect(service._toNumber('1')).toEqual(1); + expect(service._toNumber(1)).toEqual(1); expect(service._toNumber('1.1')).toEqual(1.1); }); - it("correctly returns undefined for non-numbers", function() { + it('correctly returns undefined for non-numbers', function() { expect(service._toNumber('a')).toEqual(undefined); expect(service._toNumber('')).toEqual(undefined); expect(service._toNumber(undefined)).toEqual(undefined); @@ -154,13 +157,13 @@ define([ /** * Spec for testing _getBaselayerFilters(). */ - describe("_getBaselayerFilters()", function() { + describe('_getBaselayerFilters()', function() { beforeEach(function() { service = new PlaceService({}, {}); }); - it("correctly returns filter for single layer", function() { + it('correctly returns filter for single layer', function() { var f1 = {slug: '1', category_slug: 'forest_clearing'}; var f2 = {slug: '2', category_slug: 'forest_clearing'}; @@ -174,13 +177,13 @@ define([ /** * Spec for testing _getSublayerFilters(). */ - describe("_getSublayerFilters()", function() { + describe('_getSublayerFilters()', function() { beforeEach(function() { service = new PlaceService({}, {}); }); - it("correctly returns filter for single layer", function() { + it('correctly returns filter for single layer', function() { var f1 = {id: '1'}; var f2 = {id: '2'}; @@ -194,13 +197,13 @@ define([ /** * Spec for testing _standardizeParams(). */ - describe("_standardizeParams()", function() { + describe('_standardizeParams()', function() { beforeEach(function() { service = new PlaceService({}, {}); }); - it("correctly standardizes input parameters", function() { + it('correctly standardizes input parameters', function() { var resultParams = service._standardizeParams(params); expect(resultParams).toEqual(jasmine.objectContaining({ @@ -214,7 +217,7 @@ define([ })); }); - it("correctly adds a default ALL iso parameter", function() { + it('correctly adds a default ALL iso parameter', function() { var testParams = _.omit(params, 'iso'); var resultParams = service._standardizeParams(testParams); @@ -232,7 +235,7 @@ define([ - describe("Test Place/register event", function() { + describe('Test Place/register event', function() { var presenter = {name: 'presenter'}; beforeEach(function() { @@ -240,7 +243,7 @@ define([ mps.publish('Place/register', [presenter]); }); - it("Presenter registered", function() { + it('Presenter registered', function() { expect(service._presenters).toEqual([presenter]); }); }); From 6efbe104f084940591fce47a105082c95a9d7d5f Mon Sep 17 00:00:00 2001 From: Pedro Garcia Date: Fri, 27 Jun 2014 10:02:33 +0200 Subject: [PATCH 186/823] tonumber _.mixin --- .../javascripts/map/services/PlaceService.js | 27 ++-------- app/assets/javascripts/map/utils.js | 12 +++++ app/assets/stylesheets/application.scss | 49 +++++++++---------- 3 files changed, 41 insertions(+), 47 deletions(-) diff --git a/app/assets/javascripts/map/services/PlaceService.js b/app/assets/javascripts/map/services/PlaceService.js index 31643bf90b..a0ff3f0887 100644 --- a/app/assets/javascripts/map/services/PlaceService.js +++ b/app/assets/javascripts/map/services/PlaceService.js @@ -134,12 +134,12 @@ define([ */ _standardizeParams: function(params) { var p = _.clone(params); - p.zoom = this._toNumber(params.zoom); - p.lat = this._toNumber(params.lat); - p.lng = this._toNumber(params.lng); + p.zoom = _.toNumber(params.zoom); + p.lat = _.toNumber(params.lat); + p.lng = _.toNumber(params.lng); p.maptype = params.maptype; - p.begin = this._toNumber(params.begin); - p.end = this._toNumber(params.end); + p.begin = _.toNumber(params.begin); + p.end = _.toNumber(params.end); p.iso = params.iso || 'ALL'; return p; }, @@ -161,23 +161,6 @@ define([ return params; }, - - /** - * Returns supplied value as a Number or as undefined if it's not a number. - * - * @param {string} val The value to convert - * @return {Number|undefined} The converted Number or undefined - */ - _toNumber: function(val) { - if ((val === undefined || val === null || String(val).trim() === '')) { - return undefined; - } else if (isNaN(val)) { - return undefined; - } else { - return Number(val); - } - }, - /** * Return array of filter objects {slug:, category_slug:} for baselayers. * diff --git a/app/assets/javascripts/map/utils.js b/app/assets/javascripts/map/utils.js index c46e72260b..2aaa01687e 100644 --- a/app/assets/javascripts/map/utils.js +++ b/app/assets/javascripts/map/utils.js @@ -44,4 +44,16 @@ require([ return urlParams; } }); + + _.mixin({ + toNumber: function(val) { + if ((val === undefined || val === null || String(val).trim() === '')) { + return undefined; + } else if (isNaN(val)) { + return undefined; + } else { + return Number(val); + } + } + }) }); diff --git a/app/assets/stylesheets/application.scss b/app/assets/stylesheets/application.scss index 8290cc2bba..fe49403832 100644 --- a/app/assets/stylesheets/application.scss +++ b/app/assets/stylesheets/application.scss @@ -3,36 +3,35 @@ //= require tipsy // Compass -@import 'compass/reset'; +@import "compass/reset"; @import "compass"; @import "compass/css3"; // General -@import '_settings'; -@import '_mixins'; -@import '_layout'; -@import '_grid'; -@import '_base'; +@import "_settings"; +@import "_mixins"; +@import "_layout"; +@import "_grid"; +@import "_base"; // Images -@import 'icons/*.png'; -@import 'logos/*.png'; -@import 'home-icons/*.png'; -@import 'static-icons/*.png'; -@import 'static-logos/*.png'; -@import 'country-icons/*.png'; -@import 'stories-icons/*.png'; -/**/ +@import "icons/*.png"; +@import "logos/*.png"; +@import "home-icons/*.png"; +@import "static-icons/*.png"; +@import "static-logos/*.png"; +@import "country-icons/*.png"; +@import "stories-icons/*.png"; // Modules -@import 'modules/_mod-header-nav'; -@import 'modules/_mod-layers-menu'; -@import 'modules/_mod-form'; -// @import 'modules/_mod-map'; -@import 'modules/_mod-widget'; -@import 'modules/_mod-widget-legend'; -@import 'modules/_mod-widget-searchbox'; -@import 'modules/_mod-widget-maptype-selector'; -@import 'modules/_mod-timeline'; -// @import 'modules/countries'; -// @import 'modules/home'; \ No newline at end of file +@import "modules/_mod-header-nav"; +@import "modules/_mod-layers-menu"; +@import "modules/_mod-form"; +// @import "modules/_mod-map"; +@import "modules/_mod-widget"; +@import "modules/_mod-widget-legend"; +@import "modules/_mod-widget-searchbox"; +@import "modules/_mod-widget-maptype-selector"; +@import "modules/_mod-timeline"; +// @import "modules/countries"; +// @import "modules/home"; \ No newline at end of file From b2faba4c87d1a5bd1e72a7ec72d9d1b2083fb7ec Mon Sep 17 00:00:00 2001 From: David Inga Date: Fri, 27 Jun 2014 10:41:28 +0200 Subject: [PATCH 187/823] added editorconfig and format url params --- .editorconfig | 11 +++ .../javascripts/map/services/PlaceService.js | 99 +++++++++++-------- 2 files changed, 68 insertions(+), 42 deletions(-) create mode 100644 .editorconfig diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000000..66e26a7af2 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,11 @@ +# http://EditorConfig.org + +root = true + +[*] +indent_style = space +indent_size = 2 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true diff --git a/app/assets/javascripts/map/services/PlaceService.js b/app/assets/javascripts/map/services/PlaceService.js index a0ff3f0887..d082ba90bf 100644 --- a/app/assets/javascripts/map/services/PlaceService.js +++ b/app/assets/javascripts/map/services/PlaceService.js @@ -1,13 +1,13 @@ /** * The PlaceService class manages places in the application. * - * A place is just the current state of the application which can be + * A place is just the current state of the application which can be * represented as an Object or a URL. For example, the place associated with: * * http://localhost:5000/map/6/2/17/ALL/terrain/loss * * Can also be represented like this: - * + * * zoom - 6 * lat - 2 * lng - 17 @@ -18,28 +18,28 @@ * The PlaceService class handles the following use cases: * * 1) New route updates views - * + * * The Router receives a new URL and all application views need to be updated - * with the state encoded in the URL. - * - * Here the router publishes the "Place/update" event passing in the route - * name and route parameters. The PlaceService handles the event by - * standardizing the route parameters and publishing them in a "Place/go" - * event. Any presenters listening to the event receives the updated + * with the state encoded in the URL. + * + * Here the router publishes the "Place/update" event passing in the route + * name and route parameters. The PlaceService handles the event by + * standardizing the route parameters and publishing them in a "Place/go" + * event. Any presenters listening to the event receives the updated * application state and can update their views. * * 2) Updated view updates URL - * - * A View state changes (e.g., a new map zoom) and the URL needs to be - * updated, not only with its new state, but from the stae of all views in - * the application that provide state for URLs. - * - * Here presenters publishe the "Place/register" event passing in a - * reference to themselves. The PlaceService subscribes to the - * "Place/register" event so that it can keep references to all presenters + * + * A View state changes (e.g., a new map zoom) and the URL needs to be + * updated, not only with its new state, but from the stae of all views in + * the application that provide state for URLs. + * + * Here presenters publishe the "Place/register" event passing in a + * reference to themselves. The PlaceService subscribes to the + * "Place/register" event so that it can keep references to all presenters * that provide state. Then the view publishes the "Place/update" event - * passing in a "go" parameter. If "go" is false, the PlaceService will - * update the URL. Otherwise it will publish the "Place/go" event which will + * passing in a "go" parameter. If "go" is false, the PlaceService will + * update the URL. Otherwise it will publish the "Place/go" event which will * notify all subscribed presenters. * * @return {PlaceService} The PlaceService class @@ -59,9 +59,9 @@ define([ _uriTemplate: 'map{/zoom}{/lat}{/lng}{/iso}{/maptype}{/baselayers}{/sublayers}{?begin,end}', /** - * Create new PlaceService with supplied MapLayerService and + * Create new PlaceService with supplied MapLayerService and * Backbone.Router. - * + * * @param {MapLayerService} mapLayerService Instance of MapLayerService * @param {Backbond.Router} router Instance of Backbone.Router * @return {PlaceService} Instance of PlaceService @@ -89,10 +89,10 @@ define([ /** * Handles a new place. * - * If go is true, fires a Place/go event passing in the place parameters - * which will include layers retrieved from the MapLayerService. Otherwise + * If go is true, fires a Place/go event passing in the place parameters + * which will include layers retrieved from the MapLayerService. Otherwise * the URL is silently updated with a new route. - * + * * @param {string} name The place name * @param {Object} params The place parameters * @param {[type]} go True to publish Place/go event, false to update URL @@ -111,24 +111,39 @@ define([ if (go) { this._getMapLayers( params.baselayers, - params.sublayers, + params.sublayers, _.bind(function(layers) { newPlace.params.layers = layers; - mps.publish('Place/go', [newPlace]); + mps.publish('Place/go', [newPlace]); }, this)); - } else { - route = this._getRoute(newPlace.params); - this.router.navigate(route, {silent: true}); } + + route = this._getRoute(newPlace.params); + this.router.navigate(route, {silent: true}); + }, + + /** + * Return formated url representation of supplied params object + * to keep a precise lat/lng in the application but not in url. + * + * @param {Object} params The params to standardize + * @return {Object} The standardized params. + */ + _formatUrl: function(params) { + return _.extend({}, params, { + lat: params.lat.toFixed(2); + lng: params.lng.toFixed(2); + }); }, _getRoute: function(params) { + params = formatUrl(params); return new UriTemplate(this._uriTemplate).fillFromObject(params); }, /** * Return standardized representation of supplied params object. - * + * * @param {Object} params The params to standardize * @return {Object} The standardized params. */ @@ -145,11 +160,11 @@ define([ }, /** - * Return param object representing state from all registered presenters + * Return param object representing state from all registered presenters * that implement getPlaceParams(). - * + * * @param {Array} presenters The registered presenters - * @return {Object} Params representing state from all presenters + * @return {Object} Params representing state from all presenters */ _getPresenterParams: function(presenters) { var params = {}; @@ -163,37 +178,37 @@ define([ /** * Return array of filter objects {slug:, category_slug:} for baselayers. - * + * * @param {string} layers CSV list of baselayer slug names - * @return {Array} Filter objects for baselayers + * @return {Array} Filter objects for baselayers */ _getBaselayerFilters: function(layers) { var baselayers = layers ? layers.split(',') : []; var filters = _.map(baselayers, function (name) { return {slug: name, category_slug: 'forest_clearing'}; }); - + return filters; }, /** * Return array of filter objects {id:} for sublayers. - * + * * @param {string} layers CSV list of sublayer ids - * @return {Array} Filter objects for sublayers + * @return {Array} Filter objects for sublayers */ _getSublayerFilters: function(layers) { var sublayers = layers ? layers.split(',') : []; var filters = _.map(sublayers, function(id) { return {id: id}; }); - + return filters; }, /** * Asynchronously get map layers objects from MapLayerService. - * + * * @param {string} baselayers CSV list of baselayer slug names * @param {string} sublayers CSV list of sublayer ids * @param {Function} callback Called with layers @@ -215,6 +230,6 @@ define([ }, this)); } }); - + return PlaceService; -}); \ No newline at end of file +}); From 9de768e4795b3a3baab7299f4756cc40a0449c2f Mon Sep 17 00:00:00 2001 From: Pedro Garcia Date: Fri, 27 Jun 2014 10:56:54 +0200 Subject: [PATCH 188/823] Timeline presenter --- .../map/presenters/TimelinePresenter.js | 50 ++++++++++++++++++ .../map/presenters/UMDLossLayerPresenter.js | 2 +- app/assets/javascripts/map/router.js | 2 +- app/assets/javascripts/map/utils.js | 2 +- app/assets/javascripts/map/views/timeline.js | 51 +++++++++---------- 5 files changed, 77 insertions(+), 30 deletions(-) create mode 100644 app/assets/javascripts/map/presenters/TimelinePresenter.js diff --git a/app/assets/javascripts/map/presenters/TimelinePresenter.js b/app/assets/javascripts/map/presenters/TimelinePresenter.js new file mode 100644 index 0000000000..f84343f6d8 --- /dev/null +++ b/app/assets/javascripts/map/presenters/TimelinePresenter.js @@ -0,0 +1,50 @@ +/** + * The Timeline view presenter. + * + * @return TimelinePresenter class + */ +define([ + 'Class', + 'underscore', + 'mps' +], function(Class, _, mps) { + + 'use strict'; + + var TimelinePresenter = Class.extend({ + + init: function(view) { + this.view = view; + this._subscribe(); + }, + + /** + * Subscribe to application events. + */ + _subscribe: function() { + mps.subscribe('Place/go', _.bind(function(place) { + // if (place.name === 'map') { + // } + }, this)); + + mps.publish('Place/register', [this]); + }, + + /** + * Used by Timelineview to delegate timeline date UI events. + * Results in the Timeline/date-change evnet getting published with + * the new lat/lng. + * + * @param {number} lat latitude + * @param {number} lng longitude + */ + updateTimelineDate: function(lat, lng) { + mps.publish('Timeline/date-change', [lat, lng]); + mps.publish('Place/update', [{go: false}]); + } + + }); + + return TimelinePresenter; + +}); diff --git a/app/assets/javascripts/map/presenters/UMDLossLayerPresenter.js b/app/assets/javascripts/map/presenters/UMDLossLayerPresenter.js index d28238c9a8..dac69988da 100644 --- a/app/assets/javascripts/map/presenters/UMDLossLayerPresenter.js +++ b/app/assets/javascripts/map/presenters/UMDLossLayerPresenter.js @@ -22,7 +22,7 @@ define([ * Subscribe to application events. */ subscribe: function() { - mps.subscribe('Timeline/change', _.bind(function(name, dates) { + mps.subscribe('Timeline/date-change', _.bind(function(name, dates) { if (this.view.getName() === name) { this.view.setTimelineDate(dates); this.view.updateTiles(); diff --git a/app/assets/javascripts/map/router.js b/app/assets/javascripts/map/router.js index 1a69b0be29..c2318e4587 100644 --- a/app/assets/javascripts/map/router.js +++ b/app/assets/javascripts/map/router.js @@ -62,7 +62,7 @@ define([ $map = $('#map'); $map.height(dh - 69); - $('.header-nav__logo').css({ position: 'absolute', top: 69 }) + $('.header-nav__logo').css({ position: 'absolute', top: 69 }); setTimeout(function() { $('html, body').scrollTop(69); }, 500); diff --git a/app/assets/javascripts/map/utils.js b/app/assets/javascripts/map/utils.js index 2aaa01687e..30da1ee0a6 100644 --- a/app/assets/javascripts/map/utils.js +++ b/app/assets/javascripts/map/utils.js @@ -55,5 +55,5 @@ require([ return Number(val); } } - }) + }); }); diff --git a/app/assets/javascripts/map/views/timeline.js b/app/assets/javascripts/map/views/timeline.js index 37163b6711..62adfa260f 100644 --- a/app/assets/javascripts/map/views/timeline.js +++ b/app/assets/javascripts/map/views/timeline.js @@ -1,32 +1,33 @@ /** - * The timeline module. + * The Timeline view module. * * Timeline for all layers configured by setting layer-specific options. * - * @return Timeline class (extends Backbone.View). + * @return Timeline view (extends Backbone.View). */ define([ 'underscore', 'backbone', - 'presenter', 'moment', 'd3', + 'presenters/TimelinePresenter', 'text!map/templates/timeline.html' -], function(_, Backbone, presenter, moment, d3, timelineTpl) { +], function(_, Backbone, moment, d3, Presenter, timelineTpl) { 'use strict'; - var Timeline = Backbone.View.extend({ + var TimelineView = Backbone.View.extend({ className: 'timeline timeline-date-range', template: _.template(timelineTpl), events: { - 'click .play': 'onClickPlay' + 'click .play': 'togglePlay' }, initialize: function(opts) { _.bindAll(this, 'onAnimate', 'onBrush', 'onBrushEnd'); + this.presenter = new Presenter(); this.opts = _.extend({ dateRange: [moment([2001]), moment()], @@ -67,7 +68,12 @@ define([ this.render(); }, + /** + * Render d3 timeline slider. + */ render: function() { + var self = this; + this.$el.html(this.template()); $('.map-container').append(this.el); @@ -77,14 +83,7 @@ define([ this.$stopIcon = this.$el.find('.stop-icon'); this.$time = this.$el.find('.time'); - this.renderSlider(); - }, - - /** - * Render d3 timeline slider. - */ - renderSlider: function() { - var self = this; + // SVG options var margin = {top: 0, right: 30, bottom: 0, left: 30}; var width = 949 - margin.left - margin.right; var height = 50 - margin.bottom - margin.top; @@ -108,14 +107,14 @@ define([ self.onBrushEnd(this); }); - // Set top svg + // Set SVG this.svg = d3.select(this.$time[0]).append('svg') .attr('width', width + margin.left + margin.right) .attr('height', height + margin.top + margin.bottom) .append('g') .attr('transform', 'translate(' + margin.left + ',' + margin.top + ')'); - // Bar, years + // xAxis this.svg.append('g') .attr('class', 'xaxis') .attr('transform', 'translate(0, ' + (height / 2) + ')') @@ -205,12 +204,8 @@ define([ /** * Event fired when user clicks play/stop button. */ - onClickPlay: function() { - if (this.playing) { - this.stopAnimation(); - } else { - this.animate(); - } + togglePlay: function() { + (this.playing) ? this.stopAnimation() : this.animate(); }, stopAnimation: function() { @@ -401,12 +396,14 @@ define([ }, /** - * Update the timeline. + * Handles a timeline date change UI event by dispaching + * to TimelinePresenter. * - * @param {array} Date range + * @param {number} lat latitude + * @param {number} lng longitude */ - updateTimelineDate: function(timelineDate) { - presenter.set('timelineDate', timelineDate); + updateTimelineDate: function(lat, lng) { + this.presenter.updateTimelineDate(lat, lng); }, togglePlayIcon: function() { @@ -425,6 +422,6 @@ define([ } }); - return Timeline; + return TimelineView; }); From 06e9df71e62db767ee73fd665eb006bd93f41f85 Mon Sep 17 00:00:00 2001 From: Pedro Garcia Date: Fri, 27 Jun 2014 12:14:50 +0200 Subject: [PATCH 189/823] timeline dates --- .../map/presenters/TimelinePresenter.js | 7 +++--- .../map/presenters/UMDLossLayerPresenter.js | 4 ++-- .../map/views/layers/UMDLossLayerView.js | 24 ++++--------------- .../layers/{core => class}/CanvasLayerView.js | 15 ++++++++++++ .../imageLayer.js => class/ImageLayer.js} | 0 .../layers/{core => class}/cartodbLayer.js | 0 .../javascripts/map/views/layers/forest.js | 2 +- .../javascripts/map/views/layers/gain.js | 2 +- .../javascripts/map/views/layers/imazon.js | 2 +- app/assets/javascripts/map/views/timeline.js | 7 +++--- 10 files changed, 30 insertions(+), 33 deletions(-) rename app/assets/javascripts/map/views/layers/{core => class}/CanvasLayerView.js (94%) rename app/assets/javascripts/map/views/layers/{core/imageLayer.js => class/ImageLayer.js} (100%) rename app/assets/javascripts/map/views/layers/{core => class}/cartodbLayer.js (100%) diff --git a/app/assets/javascripts/map/presenters/TimelinePresenter.js b/app/assets/javascripts/map/presenters/TimelinePresenter.js index f84343f6d8..fbbff9f319 100644 --- a/app/assets/javascripts/map/presenters/TimelinePresenter.js +++ b/app/assets/javascripts/map/presenters/TimelinePresenter.js @@ -35,11 +35,10 @@ define([ * Results in the Timeline/date-change evnet getting published with * the new lat/lng. * - * @param {number} lat latitude - * @param {number} lng longitude + * @param {Array} date 2D array of moment dates [begin, end] */ - updateTimelineDate: function(lat, lng) { - mps.publish('Timeline/date-change', [lat, lng]); + updateTimelineDate: function(date) { + mps.publish('Timeline/date-change', [date]); mps.publish('Place/update', [{go: false}]); } diff --git a/app/assets/javascripts/map/presenters/UMDLossLayerPresenter.js b/app/assets/javascripts/map/presenters/UMDLossLayerPresenter.js index dac69988da..9f6f1bc9e4 100644 --- a/app/assets/javascripts/map/presenters/UMDLossLayerPresenter.js +++ b/app/assets/javascripts/map/presenters/UMDLossLayerPresenter.js @@ -22,9 +22,9 @@ define([ * Subscribe to application events. */ subscribe: function() { - mps.subscribe('Timeline/date-change', _.bind(function(name, dates) { + mps.subscribe('Timeline/date-change', _.bind(function(name, date) { if (this.view.getName() === name) { - this.view.setTimelineDate(dates); + this.view.setTimelineDate(date); this.view.updateTiles(); } }, this)); diff --git a/app/assets/javascripts/map/views/layers/UMDLossLayerView.js b/app/assets/javascripts/map/views/layers/UMDLossLayerView.js index 1593f07736..5124e16920 100644 --- a/app/assets/javascripts/map/views/layers/UMDLossLayerView.js +++ b/app/assets/javascripts/map/views/layers/UMDLossLayerView.js @@ -5,7 +5,7 @@ */ define([ 'moment', - 'views/layers/core/CanvasLayerView', + 'views/layers/class/CanvasLayerView', 'presenters/UMDLossLayerPresenter' ], function(moment, CanvasLayerView, Presenter) { @@ -19,7 +19,6 @@ define([ this.dataMaxZoom = 12; this.name = 'loss'; this.url = 'http://earthengine.google.org/static/hansen_2013/gfw_loss_year/%z/%x/%y.png'; - this.timelineDate = [moment([2001]), moment([2013])]; this.presenter = new Presenter(this); }, @@ -61,25 +60,10 @@ define([ /** * Used by UMDLoassLayerPresenter to set the dates for the tile. * - * @param {Array} timelineDate 2D array of moment dates [begin, end] + * @param {Array} date 2D array of moment dates [begin, end] */ - setTimelineDate: function(timelineDate) { - this.timelineDate = timelineDate; - }, - - getLayer: function() { - return this.layer; - }, - - /** - * Return the view name - */ - getName: function() { - return this.layer.name; - }, - - getCategory: function() { - return this.layer.category_slug; + setTimelineDate: function(date) { + this.timelineDate = date; } }); diff --git a/app/assets/javascripts/map/views/layers/core/CanvasLayerView.js b/app/assets/javascripts/map/views/layers/class/CanvasLayerView.js similarity index 94% rename from app/assets/javascripts/map/views/layers/core/CanvasLayerView.js rename to app/assets/javascripts/map/views/layers/class/CanvasLayerView.js index b904b666cd..2712ce759f 100644 --- a/app/assets/javascripts/map/views/layers/core/CanvasLayerView.js +++ b/app/assets/javascripts/map/views/layers/class/CanvasLayerView.js @@ -143,6 +143,21 @@ define([ this.filterCanvasImage(I.data, ctx.width, ctx.height, zoom); ctx.putImageData(I,0,0); } + }, + + getLayer: function() { + return this.layer; + }, + + /** + * Return the view name + */ + getName: function() { + return this.layer.name; + }, + + getCategory: function() { + return this.layer.category_slug; } }); diff --git a/app/assets/javascripts/map/views/layers/core/imageLayer.js b/app/assets/javascripts/map/views/layers/class/ImageLayer.js similarity index 100% rename from app/assets/javascripts/map/views/layers/core/imageLayer.js rename to app/assets/javascripts/map/views/layers/class/ImageLayer.js diff --git a/app/assets/javascripts/map/views/layers/core/cartodbLayer.js b/app/assets/javascripts/map/views/layers/class/cartodbLayer.js similarity index 100% rename from app/assets/javascripts/map/views/layers/core/cartodbLayer.js rename to app/assets/javascripts/map/views/layers/class/cartodbLayer.js diff --git a/app/assets/javascripts/map/views/layers/forest.js b/app/assets/javascripts/map/views/layers/forest.js index 0d6a2870bb..31b583cb1f 100644 --- a/app/assets/javascripts/map/views/layers/forest.js +++ b/app/assets/javascripts/map/views/layers/forest.js @@ -7,7 +7,7 @@ define([ 'backbone', 'mps', 'presenter', - 'views/layers/core/canvasLayer', + 'views/layers/class/canvasLayer', ], function(Backbone, mps, presenter, CanvasLayer) { 'use strict'; diff --git a/app/assets/javascripts/map/views/layers/gain.js b/app/assets/javascripts/map/views/layers/gain.js index d28e83ecaf..7295d310e9 100644 --- a/app/assets/javascripts/map/views/layers/gain.js +++ b/app/assets/javascripts/map/views/layers/gain.js @@ -5,7 +5,7 @@ */ define([ 'backbone', - 'views/layers/core/imageLayer', + 'views/layers/class/imageLayer', 'views/timeline', 'moment' ], function(Backbone, ImageLayer) { diff --git a/app/assets/javascripts/map/views/layers/imazon.js b/app/assets/javascripts/map/views/layers/imazon.js index 61278dd4b4..5017aa34fb 100644 --- a/app/assets/javascripts/map/views/layers/imazon.js +++ b/app/assets/javascripts/map/views/layers/imazon.js @@ -5,7 +5,7 @@ */ define([ 'backbone', - 'views/layers/core/cartodbLayer', + 'views/layers/class/cartodbLayer', 'views/timeline', 'moment' ], function(Backbone, CartodbLayer, Timeline, moment) { diff --git a/app/assets/javascripts/map/views/timeline.js b/app/assets/javascripts/map/views/timeline.js index 62adfa260f..5ea35486da 100644 --- a/app/assets/javascripts/map/views/timeline.js +++ b/app/assets/javascripts/map/views/timeline.js @@ -399,11 +399,10 @@ define([ * Handles a timeline date change UI event by dispaching * to TimelinePresenter. * - * @param {number} lat latitude - * @param {number} lng longitude + * @param {Array} timelineDate 2D array of moment dates [begin, end] */ - updateTimelineDate: function(lat, lng) { - this.presenter.updateTimelineDate(lat, lng); + updateTimelineDate: function(date) { + this.presenter.updateTimelineDate(date); }, togglePlayIcon: function() { From 951b1e97f9aa51a1dcb614c1116ba099ea929605 Mon Sep 17 00:00:00 2001 From: Pedro Garcia Date: Fri, 27 Jun 2014 12:38:59 +0200 Subject: [PATCH 190/823] hotfixes --- .../javascripts/map/services/PlaceService.js | 1 - .../map/views/layers/UMDLossLayerView.js | 2 +- .../map/views/layers/class/CanvasLayerView.js | 30 ++++++++++++++----- 3 files changed, 24 insertions(+), 9 deletions(-) diff --git a/app/assets/javascripts/map/services/PlaceService.js b/app/assets/javascripts/map/services/PlaceService.js index a0ff3f0887..519968e09f 100644 --- a/app/assets/javascripts/map/services/PlaceService.js +++ b/app/assets/javascripts/map/services/PlaceService.js @@ -55,7 +55,6 @@ define([ var PlaceService = Class.extend({ - /*jshint multistr: true */ _uriTemplate: 'map{/zoom}{/lat}{/lng}{/iso}{/maptype}{/baselayers}{/sublayers}{?begin,end}', /** diff --git a/app/assets/javascripts/map/views/layers/UMDLossLayerView.js b/app/assets/javascripts/map/views/layers/UMDLossLayerView.js index 5124e16920..e0cce5381a 100644 --- a/app/assets/javascripts/map/views/layers/UMDLossLayerView.js +++ b/app/assets/javascripts/map/views/layers/UMDLossLayerView.js @@ -18,7 +18,7 @@ define([ this.layer = layer; this.dataMaxZoom = 12; this.name = 'loss'; - this.url = 'http://earthengine.google.org/static/hansen_2013/gfw_loss_year/%z/%x/%y.png'; + this.urlTemplate = 'http://earthengine.google.org/static/hansen_2013/gfw_loss_year{/z}{/x}{/y}.png'; this.presenter = new Presenter(this); }, diff --git a/app/assets/javascripts/map/views/layers/class/CanvasLayerView.js b/app/assets/javascripts/map/views/layers/class/CanvasLayerView.js index 2712ce759f..b8ad8b765e 100644 --- a/app/assets/javascripts/map/views/layers/class/CanvasLayerView.js +++ b/app/assets/javascripts/map/views/layers/class/CanvasLayerView.js @@ -5,7 +5,8 @@ */ define([ 'Class', -], function(Class) { + 'uri' +], function(Class, UriTemplate) { var CanvasLayer = Class.extend({ @@ -68,7 +69,8 @@ define([ } } - var url = this.url.replace('%z', z).replace('%x', x).replace('%y', y); + var params = {z: z, x: x, y: y}); + var url = new UriTemplate(this._urlTemplate).fillFromObject(urlParams); xhr.onload = function () { var url = URL.createObjectURL(this.response), @@ -98,22 +100,30 @@ define([ /** * Filters the canvas image. Subclasses implement this. * - * @param {[type]} imgdata [description] - * @param {[type]} w [description] - * @param {[type]} h [description] - * @param {[type]} zoom [description] - * @return {[type]} [description] + * @param {object} imgdata + * @param {integer} w width + * @param {integer} h height + * @param {integer} zoom */ filterCanvasImage: function(imgdata, w, h, zoom) { // NOOP }, + /** + * Update current tiles by calling this.filterTile(). + */ updateTiles: function() { for(var i in this.tiles) { this.filterTile(this.tiles[i]); } }, + /** + * Filter canvas tile. + * + * @param {canvas} canvas + * @param {integer} zoom + */ filterTile: function(canvas, zoom) { var ctx = canvas.getContext('2d'); coord = canvas.coord; @@ -145,6 +155,9 @@ define([ } }, + /** + * Return the layer + */ getLayer: function() { return this.layer; }, @@ -156,6 +169,9 @@ define([ return this.layer.name; }, + /** + * Return the layer category slug + */ getCategory: function() { return this.layer.category_slug; } From af31b43c90682697a60db0ba5aa1f7ca241f347b Mon Sep 17 00:00:00 2001 From: Pedro Garcia Date: Fri, 27 Jun 2014 12:52:04 +0200 Subject: [PATCH 191/823] hot fixes --- .../map/presenters/LayersNavPresenter.js | 0 app/assets/javascripts/map/router.js | 11 ++- .../javascripts/map/services/PlaceService.js | 6 +- .../{layersMenu.html => layersNav.html} | 0 .../map/views/layers/UMDLossLayerView.js | 8 +- .../{CanvasLayerView.js => CanvasLayer.js} | 18 ++-- .../views/{layersMenu.js => layersNavView.js} | 92 +++++++++---------- 7 files changed, 68 insertions(+), 67 deletions(-) create mode 100644 app/assets/javascripts/map/presenters/LayersNavPresenter.js rename app/assets/javascripts/map/templates/{layersMenu.html => layersNav.html} (100%) rename app/assets/javascripts/map/views/layers/class/{CanvasLayerView.js => CanvasLayer.js} (97%) rename app/assets/javascripts/map/views/{layersMenu.js => layersNavView.js} (71%) diff --git a/app/assets/javascripts/map/presenters/LayersNavPresenter.js b/app/assets/javascripts/map/presenters/LayersNavPresenter.js new file mode 100644 index 0000000000..e69de29bb2 diff --git a/app/assets/javascripts/map/router.js b/app/assets/javascripts/map/router.js index c2318e4587..48c3a2c371 100644 --- a/app/assets/javascripts/map/router.js +++ b/app/assets/javascripts/map/router.js @@ -2,7 +2,7 @@ * The router module. * * Router handles app routing and URL parameters and updates Presenter. - * + * * @return singleton instance of Router class (extends Backbone.Router). */ define([ @@ -11,13 +11,14 @@ define([ 'backbone', 'mps', 'gmap', + 'views/layersNavView', 'views/MapView', 'services/PlaceService', 'services/MapLayerService' -], function($, _, Backbone, mps, gmap, MapView, PlaceService, mapLayerService) { +], function($, _, Backbone, mps, gmap, layersNavView, MapView, PlaceService, mapLayerService) { 'use strict'; - + var Router = Backbone.Router.extend({ routes: { @@ -47,7 +48,7 @@ define([ }; var queryParams = _.parseUrl(); var params = _.extend(pathParams, queryParams); - + gmap.init(_.bind(function() { if (!this.mapView) { this.mapView = new MapView(); @@ -74,4 +75,4 @@ define([ return router; -}); \ No newline at end of file +}); diff --git a/app/assets/javascripts/map/services/PlaceService.js b/app/assets/javascripts/map/services/PlaceService.js index 8a0eac6aa1..8201e06e29 100644 --- a/app/assets/javascripts/map/services/PlaceService.js +++ b/app/assets/javascripts/map/services/PlaceService.js @@ -130,13 +130,13 @@ define([ */ _formatUrl: function(params) { return _.extend({}, params, { - lat: params.lat.toFixed(2); - lng: params.lng.toFixed(2); + lat: params.lat.toFixed(2), + lng: params.lng.toFixed(2) }); }, _getRoute: function(params) { - params = formatUrl(params); + params = this._formatUrl(params); return new UriTemplate(this._uriTemplate).fillFromObject(params); }, diff --git a/app/assets/javascripts/map/templates/layersMenu.html b/app/assets/javascripts/map/templates/layersNav.html similarity index 100% rename from app/assets/javascripts/map/templates/layersMenu.html rename to app/assets/javascripts/map/templates/layersNav.html diff --git a/app/assets/javascripts/map/views/layers/UMDLossLayerView.js b/app/assets/javascripts/map/views/layers/UMDLossLayerView.js index e0cce5381a..677acd9c3e 100644 --- a/app/assets/javascripts/map/views/layers/UMDLossLayerView.js +++ b/app/assets/javascripts/map/views/layers/UMDLossLayerView.js @@ -1,17 +1,17 @@ /** * The UMD loss map layer view. * - * @return UMDLossLayerView class (extends CanvasLayerView) + * @return UMDLossLayerView class (extends CanvasLayer) */ define([ 'moment', - 'views/layers/class/CanvasLayerView', + 'views/layers/class/CanvasLayer', 'presenters/UMDLossLayerPresenter' -], function(moment, CanvasLayerView, Presenter) { +], function(moment, CanvasLayer, Presenter) { 'use strict'; - var UMDLossLayerView = CanvasLayerView.extend({ + var UMDLossLayerView = CanvasLayer.extend({ init: function(layer) { this._super(); diff --git a/app/assets/javascripts/map/views/layers/class/CanvasLayerView.js b/app/assets/javascripts/map/views/layers/class/CanvasLayer.js similarity index 97% rename from app/assets/javascripts/map/views/layers/class/CanvasLayerView.js rename to app/assets/javascripts/map/views/layers/class/CanvasLayer.js index b8ad8b765e..2c1afdcb2a 100644 --- a/app/assets/javascripts/map/views/layers/class/CanvasLayerView.js +++ b/app/assets/javascripts/map/views/layers/class/CanvasLayer.js @@ -1,6 +1,6 @@ /** * The HTML5 Canvas map layer module. - * + * * @return CanvasLayer class (extends Class). */ define([ @@ -16,9 +16,9 @@ define([ }, /** - * Called whenever the Google Maps API determines that the map needs to + * Called whenever the Google Maps API determines that the map needs to * display new tiles for the given viewport. - * + * * @param {[type]} coord [description] * @param {[type]} zoom [description] * @param {[type]} ownerDocument [description] @@ -36,7 +36,7 @@ define([ var tileId = coord.x + '_' + coord.y + '_' + zoom; canvas.setAttribute('id', tileId); - + if (tileId in this.tiles) { delete this.tiles[tileId]; } @@ -69,7 +69,7 @@ define([ } } - var params = {z: z, x: x, y: y}); + var params = {z: z, x: x, y: y}; var url = new UriTemplate(this._urlTemplate).fillFromObject(urlParams); xhr.onload = function () { @@ -99,7 +99,7 @@ define([ /** * Filters the canvas image. Subclasses implement this. - * + * * @param {object} imgdata * @param {integer} w width * @param {integer} h height @@ -120,7 +120,7 @@ define([ /** * Filter canvas tile. - * + * * @param {canvas} canvas * @param {integer} zoom */ @@ -161,7 +161,7 @@ define([ getLayer: function() { return this.layer; }, - + /** * Return the view name */ @@ -178,4 +178,4 @@ define([ }); return CanvasLayer; -}); \ No newline at end of file +}); diff --git a/app/assets/javascripts/map/views/layersMenu.js b/app/assets/javascripts/map/views/layersNavView.js similarity index 71% rename from app/assets/javascripts/map/views/layersMenu.js rename to app/assets/javascripts/map/views/layersNavView.js index 1da3ee1498..9fbdea863f 100644 --- a/app/assets/javascripts/map/views/layersMenu.js +++ b/app/assets/javascripts/map/views/layersNavView.js @@ -1,46 +1,46 @@ -/** - * The layers filter module. - * - * @return singleton instance of layers fitler class (extends Backbone.View). - */ -define([ - 'backbone', - 'underscore', - 'text!map/templates/layersMenu.html' -], function(Backbone, _, layersMenuTpl) { - - 'use strict'; - - var LayersMenu = Backbone.View.extend({ - - el: '.layers-menu', - template: _.template(layersMenuTpl), - - events: { - 'click .layer-title': 'onClickLayer', - 'click .radio': 'onClickLayer' - }, - - initialize: function() { - this.render(); - }, - - render: function() { - this.$el.append(this.template()); - }, - - onClickLayer: function(event) { - var layerName = $(event.currentTarget).parents('li').data('layer'); - - if (layerName) { - window.mps.publish('presenter/toggle-layer', [layerName]); - } - }, - - }); - - var layersMenu = new LayersMenu(); - - return layersMenu; - -}); +/** + * The layers filter module. + * + * @return singleton instance of layers fitler class (extends Backbone.View). + */ +define([ + 'backbone', + 'underscore', + 'text!map/templates/layersNav.html' +], function(Backbone, _, layersNavTpl) { + + 'use strict'; + + var LayersNavView = Backbone.View.extend({ + + el: '.layers-menu', + template: _.template(layersNavTpl), + + events: { + 'click .layer-title': 'onClickLayer', + 'click .radio': 'onClickLayer' + }, + + initialize: function() { + this.render(); + }, + + render: function() { + this.$el.append(this.template()); + }, + + onClickLayer: function(event) { + var layerName = $(event.currentTarget).parents('li').data('layer'); + + if (layerName) { + window.mps.publish('presenter/toggle-layer', [layerName]); + } + }, + + }); + + var layersNavView = new LayersNavView(); + + return layersNavView; + +}); From 7adedff3d520412389fb48c126b862a66f86f873 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A1n=20P=C3=A9rez?= Date: Fri, 27 Jun 2014 15:09:18 +0200 Subject: [PATCH 192/823] protected areas and other layers analysis --- lib/assets/javascripts/gfw/gfw_lib.js.erb | 11 +-- lib/assets/javascripts/gfw/ui/analysis.js.erb | 30 ++++---- .../javascripts/gfw/ui/infowindow.js.erb | 76 +++++++++---------- .../gfw/ui/protected-infowindow.js | 2 +- lib/assets/stylesheets/map.scss | 13 ++++ 5 files changed, 75 insertions(+), 57 deletions(-) diff --git a/lib/assets/javascripts/gfw/gfw_lib.js.erb b/lib/assets/javascripts/gfw/gfw_lib.js.erb index 225dc54e58..5beb179152 100644 --- a/lib/assets/javascripts/gfw/gfw_lib.js.erb +++ b/lib/assets/javascripts/gfw/gfw_lib.js.erb @@ -679,18 +679,19 @@ GFW.modules.app = function(gfw) { var pair = data.cartodb_id.split(':'); if (pair[1] === 'world_ifl') return; - //here i make a crude request for the columns of the table //nulling out the geoms to save payload - var request_sql = 'SELECT *, null as the_geom, null as the_geom_webmercator FROM ' + pair[1] + ' WHERE cartodb_id = ' + pair[0]; + var request_sql = 'SELECT *, ST_AsGeoJSON(the_geom) as the_geom, null as the_geom_webmercator, \'' + pair[1] + '\' as table FROM ' + pair[1] + ' WHERE cartodb_id = ' + pair[0]; var url = 'http://dyynnn89u7nkm.cloudfront.net/api/v2/sql?q=' + encodeURIComponent(request_sql); - + var makeSuccessCallback = function(pairs) { return function(json) { + + if (!json || (json && !json.rows)) return; - delete json.rows[0]['cartodb_id'], - delete json.rows[0]['the_geom']; + //delete json.rows[0]['cartodb_id'], + //delete json.rows[0]['the_geom']; delete json.rows[0]['the_geom_webmercator']; delete json.rows[0]['created_at']; delete json.rows[0]['updated_at']; diff --git a/lib/assets/javascripts/gfw/ui/analysis.js.erb b/lib/assets/javascripts/gfw/ui/analysis.js.erb index bc29f37e43..e96eee407f 100644 --- a/lib/assets/javascripts/gfw/ui/analysis.js.erb +++ b/lib/assets/javascripts/gfw/ui/analysis.js.erb @@ -299,9 +299,7 @@ gfw.ui.view.Analysis = cdb.core.View.extend({ } var style = config.OVERLAYSTYLES; style.editable = false; - var features = new GeoJSON(the_geom, style); - if (Array.isArray(features)) { // countries for (var i in features) { if (features[i].length > 0) { @@ -387,7 +385,6 @@ gfw.ui.view.Analysis = cdb.core.View.extend({ baselayer = config.BASELAYER, baseurl = "//<%= ENV['GFW_API_HOST'] %>/datasets/", range; - if (config.BASELAYER === null) return; if (!this.initStats) { this.$el.find('.stats .title, .stats ul').hide(); @@ -400,7 +397,7 @@ gfw.ui.view.Analysis = cdb.core.View.extend({ this.info.setDraggable(true); - var dataset = (baselayer.indexOf('loss') === 0 ) ? 'umd' : baselayer; + var dataset = (baselayer && baselayer.indexOf('loss') === 0 ) ? 'umd' : baselayer; if (!this.range && url('?begin') && url('?end')) { this.range = '&begin=' + url('?begin') + '&end=' + url('?end'); @@ -433,13 +430,14 @@ gfw.ui.view.Analysis = cdb.core.View.extend({ iend = '2013-0-10-01' || url('?end'); range = this.range || '&begin=' + ibegin + '&end=' + iend; - } if (baselayer.indexOf('loss') === 0) { + } if (baselayer && baselayer.indexOf('loss') === 0) { var lbegin = '2001' || url('?begin'), lend = '2013' || url('?end'); range = this.range || '&begin=' + lbegin +'&end=' + lend; + } else { + range = ''; } - config.MAPOPTIONS.analysis = (not_analyze) ? '?'+range.substr(1) : ((elem) ? '?'+elem+range : '?geom='+encodeURI(area)+range); if (config.ISO === 'ALL') { @@ -463,6 +461,7 @@ gfw.ui.view.Analysis = cdb.core.View.extend({ } if (this.$el.find('.no_data_available').length > 0) this.$el.find('.no_data_available').remove(); if (no_data) { + debugger; this.info.render(); this.$el.find('.stats .spinner').fadeOut(150, function() { that.$el.find('.analysis_info .stats').append('

    No data available

    ').fadeIn(150); @@ -592,21 +591,27 @@ gfw.ui.view.Analysis = cdb.core.View.extend({ }); }, - loadProtectedAreaByWpaid: function(wdpaid,the_geom) { + loadProtectedAreaByWpaid: function(id,the_geom,table) { var that = this; - var query = "https://wri-01.cartodb.com/api/v2/sql?q=SELECT shape_area FROM wdpa_all WHERE wdpaid =" + wdpaid; + if (table === 'wdpa_all') { + var id_type = 'wdpaid', + query = "https://wri-01.cartodb.com/api/v2/sql?q=SELECT shape_area FROM " + table + " WHERE " + id_type + " =" + id; + } else { + var id_type = 'cartodb_id', + query = "https://wri-01.cartodb.com/api/v2/sql?q=SELECT ST_AsGeoJSON(the_geom) as the_geom FROM " + table + " WHERE " + id_type + " =" + id; + } $.ajax({ url: query, dataType: 'jsonp', success: function(area) { if (area.total_rows > 0 && area.rows[0].shape_area !== undefined) { - that.loadPolygon(the_geom, [area.rows[0].shape_area, 'protected_area=' + wdpaid]); + that.loadPolygon(the_geom, [area.rows[0].shape_area, 'protected_area=' + id]); } else{ that._getAlertCount(null,true); - return 'API: No area data for ' + wdpaid; + return 'API: No area data for ' + id; } }, error: function() { @@ -753,10 +758,9 @@ gfw.ui.view.Analysis = cdb.core.View.extend({ } }, - _loadProtectedArea: function(the_geom,area_id){ - debugger; + _loadUseConservationAreas: function(the_geom,area_id,name,table){ this.previous_layers = config.MAPOPTIONS.layers; - this.loadProtectedAreaByWpaid(area_id, the_geom); + this.loadProtectedAreaByWpaid(area_id, the_geom, table); }, render: function() { diff --git a/lib/assets/javascripts/gfw/ui/infowindow.js.erb b/lib/assets/javascripts/gfw/ui/infowindow.js.erb index faab768d98..00e7cf199f 100644 --- a/lib/assets/javascripts/gfw/ui/infowindow.js.erb +++ b/lib/assets/javascripts/gfw/ui/infowindow.js.erb @@ -1,4 +1,7 @@ function CartoDBInfowindow(map, opts) { + + this.content = {}; + this.latlng_ = null; this.offsetHorizontal_ = 0; this.offsetVertical_ = -10; @@ -44,7 +47,8 @@ CartoDBInfowindow.prototype.draw = function() { this.template_base = ''+ ''+ '
    '+ @@ -53,14 +57,6 @@ CartoDBInfowindow.prototype.draw = function() { div.innerHTML = this.template_image || this.template_base; var a = this.getElementsByClassName("close", div)[0]; - var analyse = this.getElementsByClassName("analyse", div); - - google.maps.event.addDomListener(analyse, 'click', function (ev) { - ev.preventDefault ? ev.preventDefault() : ev.returnValue = false; - Analysis._loadProtectedArea(me.content.geom.the_geom, me.content.id, me.content.name); - GFW.app._removeSpecialLayer(); - me._hide(); - }); google.maps.event.addDomListener(a, 'click', function (ev) { //ev.preventDefault ? ev.preventDefault() : ev.returnValue = false; @@ -145,7 +141,7 @@ CartoDBInfowindow.prototype.setOffset = function(offsetVertical, offsetHorizonta CartoDBInfowindow.prototype.setContent = function(content){ if (this.div_) { - + this.content = content; var div = this.div_, top = this.getElementsByClassName("infowindow_content", div)[0]; @@ -166,31 +162,33 @@ CartoDBInfowindow.prototype.setContent = function(content){ } for(var column in content) { - if (show_column(column, content, this.visible_columns)) { - var column_ = column, - suffix = ''; - - switch(column){ - case 'photo_credit': - column_ = 'Photo credit'; - break; - case 'Brightness': - suffix = ' (Kelvin)'; - break; - case 'Confidence': - suffix = ' (%)'; - break; - case 'Acq time': - column_ = 'Acquisition time'; - suffix = ' (UTC)'; - break; - case 'Acq date': - column_ = 'Acquisition date'; - break; + if (!(column == 'The geom' || column == 'Cartodb id')) { + if (show_column(column, content, this.visible_columns)) { + var column_ = column, + suffix = ''; + + switch(column){ + case 'photo_credit': + column_ = 'Photo credit'; + break; + case 'Brightness': + suffix = ' (Kelvin)'; + break; + case 'Confidence': + suffix = ' (%)'; + break; + case 'Acq time': + column_ = 'Acquisition time'; + suffix = ' (UTC)'; + break; + case 'Acq date': + column_ = 'Acquisition date'; + break; + } + + html += ''; + html += '

    '+(content[column] || 'empty')+ suffix + '

    '; } - - html += ''; - html += '

    '+(content[column] || 'empty')+ suffix + '

    '; } } @@ -296,12 +294,14 @@ CartoDBInfowindow.prototype._show = function() { that._createScroll() }); }); - + var me = this; $('#map').find('.protected-header .analyse').on('click', function(ev){ ev.preventDefault ? ev.preventDefault() : ev.returnValue = false; - debugger; - Analysis._loadProtectedArea(me.content.geom.the_geom, me.content.id, me.content.name); - GFW.app._removeSpecialLayer(); + Analysis._loadUseConservationAreas( $.parseJSON(me.content.the_geom) ? $.parseJSON(me.content.the_geom) : $.parseJSON(me.content['The geom']), + me.content.cartodb_id ? me.content.cartodb_id : me.content['Cartodb id'], + me.content.name ? me.content.name : me.content.Name, + me.content.table ? me.content.table : me.content.Table + ); me._hide(); }) } diff --git a/lib/assets/javascripts/gfw/ui/protected-infowindow.js b/lib/assets/javascripts/gfw/ui/protected-infowindow.js index 1a1c431a2f..88f5ddc1f1 100644 --- a/lib/assets/javascripts/gfw/ui/protected-infowindow.js +++ b/lib/assets/javascripts/gfw/ui/protected-infowindow.js @@ -52,7 +52,7 @@ ProtectedInfowindow.prototype.draw = function() { google.maps.event.addDomListener(analyse, 'click', function (ev) { ev.preventDefault ? ev.preventDefault() : ev.returnValue = false; - Analysis._loadProtectedArea(me.content.geom.the_geom, me.content.id, me.content.name); + Analysis._loadUseConservationAreas(me.content.geom.the_geom, me.content.id, me.content.name, 'wdpa_all'); GFW.app._removeSpecialLayer(); me._hide(); }); diff --git a/lib/assets/stylesheets/map.scss b/lib/assets/stylesheets/map.scss index 0a988de7bb..0ea8e2dd1e 100644 --- a/lib/assets/stylesheets/map.scss +++ b/lib/assets/stylesheets/map.scss @@ -369,6 +369,19 @@ } } + .template_base{ + .analyse { + @include position(20px, 2px, false, false); + @include size(41px, 42px); + @include mixins-sprite(graph); + + &.disabled { + @include opacity(.6); + @include pointer-events(none); + } + } + } + } } From bdd6894a7eac9ad24a7ab46a7b68932a39165e96 Mon Sep 17 00:00:00 2001 From: Aaron Date: Fri, 27 Jun 2014 08:35:46 -0700 Subject: [PATCH 193/823] Fix failing tests, add test for formatUrl --- .../javascripts/map/services/PlaceService.js | 38 +++++++++++------ .../javascripts/map/views/layersNavView.js | 2 +- jstest/spec-runner.js | 3 +- jstest/spec/PlaceService_spec.js | 41 +++++++++++-------- jstest/spec/UMDLossLayerPresenter_spec.js | 2 +- jstest/spec/utils_spec.js | 33 +++++++++++++++ 6 files changed, 86 insertions(+), 33 deletions(-) create mode 100644 jstest/spec/utils_spec.js diff --git a/app/assets/javascripts/map/services/PlaceService.js b/app/assets/javascripts/map/services/PlaceService.js index 8201e06e29..2c87b920f4 100644 --- a/app/assets/javascripts/map/services/PlaceService.js +++ b/app/assets/javascripts/map/services/PlaceService.js @@ -55,7 +55,7 @@ define([ var PlaceService = Class.extend({ - _uriTemplate: 'map{/zoom}{/lat}{/lng}{/iso}{/maptype}{/baselayers}{/sublayers}{?begin,end}', + _uriTemplate: '{name}{/zoom}{/lat}{/lng}{/iso}{/maptype}{/baselayers}{/sublayers}{?begin,end}', /** * Create new PlaceService with supplied MapLayerService and @@ -117,26 +117,38 @@ define([ }, this)); } - route = this._getRoute(newPlace.params); + route = this._getRoute(name, newPlace.params); this.router.navigate(route, {silent: true}); }, /** - * Return formated url representation of supplied params object - * to keep a precise lat/lng in the application but not in url. + * Return formated URL representation of supplied params object based on + * a route name. * - * @param {Object} params The params to standardize - * @return {Object} The standardized params. + * @param {string} name The route name + * @param {Object} params Params to standardize + * @return {Object} Params ready for URL */ - _formatUrl: function(params) { - return _.extend({}, params, { - lat: params.lat.toFixed(2), - lng: params.lng.toFixed(2) - }); + _formatUrl: function(name, params) { + if (name === 'map') { + return _.extend({}, params, { + lat: String(_.toNumber(params.lat).toFixed(2)), + lng: String(_.toNumber(params.lng).toFixed(2)) + }); + } else { + return params; + } }, - _getRoute: function(params) { - params = this._formatUrl(params); + /** + * Return route URL for supplied route name and route params. + * + * @param {string} name The route name (e.g. map) + * @param {Object} params The route params + * @return {string} The route URL + */ + _getRoute: function(name, params) { + params = _.extend(this._formatUrl(name, params), {name: name}); return new UriTemplate(this._uriTemplate).fillFromObject(params); }, diff --git a/app/assets/javascripts/map/views/layersNavView.js b/app/assets/javascripts/map/views/layersNavView.js index 9fbdea863f..922bebfaa5 100644 --- a/app/assets/javascripts/map/views/layersNavView.js +++ b/app/assets/javascripts/map/views/layersNavView.js @@ -6,7 +6,7 @@ define([ 'backbone', 'underscore', - 'text!map/templates/layersNav.html' + 'text!templates/layersNav.html' ], function(Backbone, _, layersNavTpl) { 'use strict'; diff --git a/jstest/spec-runner.js b/jstest/spec-runner.js index b8172c16b2..fd176ba7d1 100644 --- a/jstest/spec-runner.js +++ b/jstest/spec-runner.js @@ -101,7 +101,8 @@ require([ 'spec/MapLayerService_spec', 'spec/MapPresenter_spec', 'spec/UMDLossLayerPresenter_spec', - 'spec/PlaceService_spec' + 'spec/PlaceService_spec', + 'spec/utils_spec' ], function (mock_ajax, main, nsa_spec){ console.log('Setting up specs...'); }); \ No newline at end of file diff --git a/jstest/spec/PlaceService_spec.js b/jstest/spec/PlaceService_spec.js index d449110c38..b156583794 100644 --- a/jstest/spec/PlaceService_spec.js +++ b/jstest/spec/PlaceService_spec.js @@ -75,8 +75,8 @@ define([ }); it('correctly returns route', function() { - var r = 'map/8/1.1/2/idn/terrain/loss/1%2C2%2C3?begin=2014&end=3014'; - expect(service._getRoute(params)).toEqual(r); + var r = 'map/8/1.10/2.00/idn/terrain/loss/1%2C2%2C3?begin=2014&end=3014'; + expect(service._getRoute('map', params)).toEqual(r); }); }); @@ -123,36 +123,43 @@ define([ }); it('correctly calls router.navigate when go is false', function() { - var r = 'map/8/1.1/2/idn/terrain/loss/1%2C2%2C3?begin=2014&end=3014'; + var r = 'map/8/1.10/2.00/idn/terrain/loss/1%2C2%2C3?begin=2014&end=3014'; service._handleNewPlace('map', params, false); expect(mockRouter.navigate).toHaveBeenCalledWith(r, {silent: true}); }); }); - /** - * Spec for testing _toNumber(). + + /** + * Spec for testing _formatUrl(). */ - describe('_toNumber()', function() { + describe('_formatUrl()', function() { beforeEach(function() { service = new PlaceService({}, {}); }); - it('correctly returns numbers for numbers', function() { - expect(service._toNumber('1')).toEqual(1); - expect(service._toNumber(1)).toEqual(1); - expect(service._toNumber('1.1')).toEqual(1.1); + it('correctly handles lat/lng strings', function() { + expect(service._formatUrl('map', {lat: '1.234567', lng: '2.34567'})). + toEqual({lat: '1.23', lng: '2.35'}); }); + + it('correctly handles lat/lng decimals', function() { + expect(service._formatUrl('map', {lat: 1.23456789, lng: 2.3456789})). + toEqual({lat: '1.23', lng: '2.35'}); + }); - it('correctly returns undefined for non-numbers', function() { - expect(service._toNumber('a')).toEqual(undefined); - expect(service._toNumber('')).toEqual(undefined); - expect(service._toNumber(undefined)).toEqual(undefined); - expect(service._toNumber(null)).toEqual(undefined); - }); - }); + it('correctly handles lat/lng integers', function() { + expect(service._formatUrl('map', {lat: 1, lng: 2})). + toEqual({lat: '1.00', lng: '2.00'}); + }); + it('correctly handles lat/lng with non-map route name', function() { + expect(service._formatUrl('foo', {lat: 1, lng: 2})). + toEqual({lat: 1, lng: 2}); + }); + }); /** * Spec for testing _getBaselayerFilters(). diff --git a/jstest/spec/UMDLossLayerPresenter_spec.js b/jstest/spec/UMDLossLayerPresenter_spec.js index 078ac48c05..89a7a700cc 100644 --- a/jstest/spec/UMDLossLayerPresenter_spec.js +++ b/jstest/spec/UMDLossLayerPresenter_spec.js @@ -31,7 +31,7 @@ define([ }); it("Check Timeline/change event handling", function() { - mps.publish('Timeline/change', ['loss', [2001, 2002]]); + mps.publish('Timeline/date-change', ['loss', [2001, 2002]]); expect(viewSpy.setTimelineDate).toHaveBeenCalled(); expect(viewSpy.setTimelineDate).toHaveBeenCalledWith([2001, 2002]); diff --git a/jstest/spec/utils_spec.js b/jstest/spec/utils_spec.js new file mode 100644 index 0000000000..c983a48e36 --- /dev/null +++ b/jstest/spec/utils_spec.js @@ -0,0 +1,33 @@ +/** + * Unit test coverage for the utils module. + */ +define([ + 'utils', + 'underscore' +], function(utils, _) { + + 'use strict'; + + describe('utils module suite', function() { + + + /** + * Spec for testing _toNumber(). + */ + describe('_toNumber()', function() { + + it('correctly returns numbers for numbers', function() { + expect(_.toNumber('1')).toEqual(1); + expect(_.toNumber(1)).toEqual(1); + expect(_.toNumber('1.1')).toEqual(1.1); + }); + + it('correctly returns undefined for non-numbers', function() { + expect(_.toNumber('a')).toEqual(undefined); + expect(_.toNumber('')).toEqual(undefined); + expect(_.toNumber(undefined)).toEqual(undefined); + expect(_.toNumber(null)).toEqual(undefined); + }); + }); + }); +}); \ No newline at end of file From a9e17b24fda34294827fa3ce4a4442eb0aa08de5 Mon Sep 17 00:00:00 2001 From: David Inga Date: Mon, 30 Jun 2014 09:22:10 +0200 Subject: [PATCH 194/823] added bower --- .bowerrc | 3 + .gitignore | 3 +- app/assets/javascripts/map/utils.js | 2 + .../stylesheets/{modules => }/countries.scss | 27 +- .../stylesheets/{modules => }/home.scss | 25 + .../stylesheets/{modules => }/static.scss | 25 + .../{modules => }/static_public.css | 0 .../stylesheets/{modules => }/stories.scss | 25 + bower.json | 28 + config/application.rb | 2 +- config/requirejs.yml | 44 +- jstest/index.html | 4 +- jstest/spec-runner.js | 107 +- package.json | 2 +- vendor/assets/javascripts/backbone.js | 1608 --- vendor/assets/javascripts/cartodb.js | 20 - vendor/assets/javascripts/d3.js | 9255 ----------------- vendor/assets/javascripts/d3.v3.min.js | 5 - vendor/assets/javascripts/minpubsub.js | 95 - vendor/assets/javascripts/moment.js | 6 - vendor/assets/javascripts/moment.min.js | 6 - vendor/assets/javascripts/require.js | 2037 ---- vendor/assets/javascripts/text.js | 390 - vendor/assets/javascripts/underscore.js | 1399 --- 24 files changed, 217 insertions(+), 14901 deletions(-) create mode 100644 .bowerrc rename app/assets/stylesheets/{modules => }/countries.scss (98%) rename app/assets/stylesheets/{modules => }/home.scss (98%) rename app/assets/stylesheets/{modules => }/static.scss (96%) rename app/assets/stylesheets/{modules => }/static_public.css (100%) rename app/assets/stylesheets/{modules => }/stories.scss (92%) create mode 100644 bower.json delete mode 100644 vendor/assets/javascripts/backbone.js delete mode 100644 vendor/assets/javascripts/cartodb.js delete mode 100644 vendor/assets/javascripts/d3.js delete mode 100644 vendor/assets/javascripts/d3.v3.min.js delete mode 100644 vendor/assets/javascripts/minpubsub.js delete mode 100644 vendor/assets/javascripts/moment.js delete mode 100644 vendor/assets/javascripts/moment.min.js delete mode 100644 vendor/assets/javascripts/require.js delete mode 100644 vendor/assets/javascripts/text.js delete mode 100644 vendor/assets/javascripts/underscore.js diff --git a/.bowerrc b/.bowerrc new file mode 100644 index 0000000000..70f980adcd --- /dev/null +++ b/.bowerrc @@ -0,0 +1,3 @@ +{ + "directory": "vendor/assets/bower_components" +} diff --git a/.gitignore b/.gitignore index 46aca20fd2..1b0cd4fdf6 100644 --- a/.gitignore +++ b/.gitignore @@ -27,6 +27,7 @@ .idea/ .grunt node_modules +vendor/assets/bower_components # Ingore Sublime project files: -*.sublime-* \ No newline at end of file +*.sublime-* diff --git a/app/assets/javascripts/map/utils.js b/app/assets/javascripts/map/utils.js index 30da1ee0a6..ed6ef3361f 100644 --- a/app/assets/javascripts/map/utils.js +++ b/app/assets/javascripts/map/utils.js @@ -1,6 +1,7 @@ require([ 'underscore' ], function (_) { + 'use strict'; if (!String.prototype.format) { @@ -56,4 +57,5 @@ require([ } } }); + }); diff --git a/app/assets/stylesheets/modules/countries.scss b/app/assets/stylesheets/countries.scss similarity index 98% rename from app/assets/stylesheets/modules/countries.scss rename to app/assets/stylesheets/countries.scss index 6488ff32c5..6d72a6adc5 100644 --- a/app/assets/stylesheets/modules/countries.scss +++ b/app/assets/stylesheets/countries.scss @@ -1,3 +1,28 @@ +//= require jquery.qtip.min +//= require jquery.jscrollpane +//= require tipsy + +// Compass +@import "compass/reset"; +@import "compass"; +@import "compass/css3"; + +// General +@import "_settings"; +@import "_mixins"; +@import "_layout"; +@import "_grid"; +@import "_base"; + +// Images +@import "icons/*.png"; +@import "logos/*.png"; +@import "home-icons/*.png"; +@import "static-icons/*.png"; +@import "static-logos/*.png"; +@import "country-icons/*.png"; +@import "stories-icons/*.png"; + .countries { &.index .content { padding: 15px 0 56px; @@ -1637,4 +1662,4 @@ section.country_climate { span { display: none; } } } -} \ No newline at end of file +} diff --git a/app/assets/stylesheets/modules/home.scss b/app/assets/stylesheets/home.scss similarity index 98% rename from app/assets/stylesheets/modules/home.scss rename to app/assets/stylesheets/home.scss index 5b2473adb4..74c77bdaba 100644 --- a/app/assets/stylesheets/modules/home.scss +++ b/app/assets/stylesheets/home.scss @@ -1,3 +1,28 @@ +//= require jquery.qtip.min +//= require jquery.jscrollpane +//= require tipsy + +// Compass +@import "compass/reset"; +@import "compass"; +@import "compass/css3"; + +// General +@import "_settings"; +@import "_mixins"; +@import "_layout"; +@import "_grid"; +@import "_base"; + +// Images +@import "icons/*.png"; +@import "logos/*.png"; +@import "home-icons/*.png"; +@import "static-icons/*.png"; +@import "static-logos/*.png"; +@import "country-icons/*.png"; +@import "stories-icons/*.png"; + /* =Globals ----------------------------------------------- */ diff --git a/app/assets/stylesheets/modules/static.scss b/app/assets/stylesheets/static.scss similarity index 96% rename from app/assets/stylesheets/modules/static.scss rename to app/assets/stylesheets/static.scss index 273e06b0e7..7095b5e633 100644 --- a/app/assets/stylesheets/modules/static.scss +++ b/app/assets/stylesheets/static.scss @@ -1,3 +1,28 @@ +//= require jquery.qtip.min +//= require jquery.jscrollpane +//= require tipsy + +// Compass +@import "compass/reset"; +@import "compass"; +@import "compass/css3"; + +// General +@import "_settings"; +@import "_mixins"; +@import "_layout"; +@import "_grid"; +@import "_base"; + +// Images +@import "icons/*.png"; +@import "logos/*.png"; +@import "home-icons/*.png"; +@import "static-icons/*.png"; +@import "static-logos/*.png"; +@import "country-icons/*.png"; +@import "stories-icons/*.png"; + .static-inner { @extend .clearfix; background: #F2F2F3; diff --git a/app/assets/stylesheets/modules/static_public.css b/app/assets/stylesheets/static_public.css similarity index 100% rename from app/assets/stylesheets/modules/static_public.css rename to app/assets/stylesheets/static_public.css diff --git a/app/assets/stylesheets/modules/stories.scss b/app/assets/stylesheets/stories.scss similarity index 92% rename from app/assets/stylesheets/modules/stories.scss rename to app/assets/stylesheets/stories.scss index d5492cab16..9f472e518c 100644 --- a/app/assets/stylesheets/modules/stories.scss +++ b/app/assets/stylesheets/stories.scss @@ -1,3 +1,28 @@ +//= require jquery.qtip.min +//= require jquery.jscrollpane +//= require tipsy + +// Compass +@import "compass/reset"; +@import "compass"; +@import "compass/css3"; + +// General +@import "_settings"; +@import "_mixins"; +@import "_layout"; +@import "_grid"; +@import "_base"; + +// Images +@import "icons/*.png"; +@import "logos/*.png"; +@import "home-icons/*.png"; +@import "static-icons/*.png"; +@import "static-logos/*.png"; +@import "country-icons/*.png"; +@import "stories-icons/*.png"; + .stories { .header { height: 205px; diff --git a/bower.json b/bower.json new file mode 100644 index 0000000000..ec9ecc51b9 --- /dev/null +++ b/bower.json @@ -0,0 +1,28 @@ +{ + "name": "globalforestwatch", + "version": "0.1.0", + "homepage": "http://www.globalforestwatch.org", + "authors": [ + "Simbiótica " + ], + "description": "Global Forest Watch", + "license": "MIT", + "ignore": [ + "**/.*", + "node_modules", + "vendor/assets/bower_components", + "spec", + "jstests" + ], + "dependencies": { + "jquery": "1.11.1", + "underscore": "~1.6.0", + "backbone": "~1.1.2", + "minpubsub": "~0.0.2", + "d3": "~3.4.8", + "cartodb.js": "~3.10.1", + "requirejs-text": "~2.0.12", + "requirejs": "~2.1.14", + "moment": "~2.7.0" + } +} diff --git a/config/application.rb b/config/application.rb index 452057c0a3..2b3b5f0971 100644 --- a/config/application.rb +++ b/config/application.rb @@ -28,7 +28,7 @@ class Application < Rails::Application # config.i18n.default_locale = :de I18n.enforce_available_locales = false - config.assets.paths << File.join(Rails.root, 'app', 'assets', 'templates') + config.assets.paths << File.join(Rails.root, 'vendor', 'assets', 'bower_components') config.autoload_paths += %W(#{config.root}/lib) config.generators.assets = false diff --git a/config/requirejs.yml b/config/requirejs.yml index 95d34e5803..890772aede 100644 --- a/config/requirejs.yml +++ b/config/requirejs.yml @@ -2,26 +2,28 @@ modules: - name: 'map' paths: + underscore: "underscore/underscore" + backbone: "backbone/backbone" + jquery: "jquery/dist/jquery" + d3: "d3/d3" + cartodb: "cartodb.js/dist/cartodb" + moment: "moment/moment" + text: "requirejs-text/text" + mps: "minpubsub/minpubsub" + main: "map/main" - jquery: "jquery" jqueryui: "jquery-ui-1.10.4.custom.min" - underscore: "underscore" - backbone: "backbone" - mps: "minpubsub" backbonequeryparams: "backbone.queryparams" gmap: "map/gmap" - d3: "d3" uri: "uri" backbone_cartodb: "backbone.cartodb" cartodblayer: "cartodb.gmapsv3" - cartodb: "cartodb" wax: "wax.g.min" store: "store" - text: "text" Class: "class" + app: "map/app" utils: "map/utils" - moment: "moment" router: "map/router" mediator: "map/mediator" presenter: "map/presenter" @@ -35,7 +37,7 @@ paths: analysis: "map/analysis" nsa: "map/nsa" -shim: +shim: # main: # deps: # - "router" @@ -43,15 +45,15 @@ shim: # - "analysis" # exports: # "main" - underscore: + underscore: exports: "_" - backbone: + backbone: deps: - "jquery" - "underscore" exports: "Backbone" - backbone_cartodb: - deps: + backbone_cartodb: + deps: - "underscore" - "backbone" exports: "backbone_cartodb" @@ -62,15 +64,15 @@ shim: jqueryui: deps: - "jquery" - backbonequeryparams: - deps: + backbonequeryparams: + deps: - "backbone" - "underscore" exports: "backbonequeryparams" - Class: + Class: exports: "Class" - uri: - exports: "UriTemplate" - user: - deps: - - "Class" \ No newline at end of file + uri: + exports: "UriTemplate" + user: + deps: + - "Class" diff --git a/jstest/index.html b/jstest/index.html index 6d05a1e808..86919e8bdb 100644 --- a/jstest/index.html +++ b/jstest/index.html @@ -13,6 +13,6 @@

    GFW Unit Tests

    - + - \ No newline at end of file + diff --git a/jstest/spec-runner.js b/jstest/spec-runner.js index b8172c16b2..b6676576c7 100644 --- a/jstest/spec-runner.js +++ b/jstest/spec-runner.js @@ -1,49 +1,50 @@ -require.config({ +require.config({ + + baseUrl: './', - baseUrl: '..', - paths: { - main: ['app/assets/javascripts/map/main'], - jquery: ['vendor/assets/javascripts/jquery'], - underscore: ['vendor/assets/javascripts/underscore'], - backbone: ['vendor/assets/javascripts/backbone'], - mps: ['vendor/assets/javascripts/minpubsub'], - wax: ['vendor/assets/javascripts/wax.g.min'], - jqueryui: ['vendor/assets/javascripts/jquery-ui-1.10.4.custom.min'], - backbonequeryparams: ['vendor/assets/javascripts/backbone.queryparams'], - gmap: ['app/assets/javascripts/map/gmap'], - d3: ['vendor/assets/javascripts/d3'], - backbone_cartodb: ['vendor/assets/javascripts/backbone.cartodb'], - cartodb: ['vendor/assets/javascripts/cartodb'], - cartodblayer: ['vendor/assets/javascripts/cartodb.gmapsv3'], - store: ['vendor/assets/javascripts/store'], - text: ['vendor/assets/javascripts/text'], - Class: ['vendor/assets/javascripts/class'], - uri: ['vendor/assets/javascripts/uri'], - app: ['app/assets/javascripts/map/app'], - utils: ['app/assets/javascripts/map/utils'], - nsa: ['app/assets/javascripts/map/nsa'], - moment: ['vendor/assets/javascripts/moment'], - analysis: ['app/assets/javascripts/map/analysis'], - router: ['app/assets/javascripts/map/router'], - mediator: ['app/assets/javascripts/map/mediator'], - presenter: ['app/assets/javascripts/map/presenter'], - views: ['app/assets/javascripts/map/views'], - templates: ['app/assets/javascripts/map/templates'], - services: ['app/assets/javascripts/map/services'], - presenters: ['app/assets/javascripts/map/presenters'], - models: ['app/assets/javascripts/map/models'], - collections: ['app/assets/javascripts/map/collections'], - itertools: ['vendor/assets/javascripts/itertools'], - - spec: ['../jstest/spec'], - helpers: ['../jstest/helpers'], - jasmine: ['../jstest/lib/jasmine'], - jasmine_html: ['../jstest/lib/jasmine-html'], - jasmine_boot: ['../jstest/lib/boot'], - mock_ajax: ['../jstest/lib/mock-ajax'] + jquery: '../vendor/assets/bower_components/jquery/src/jquery', + underscore: '../vendor/assets/bower_components/underscore/underscore', + backbone: '../vendor/assets/bower_components/backbone/backbone', + d3: '../vendor/assets/bower_components/d3/d3', + cartodb: '../vendor/assets/bower_components/cartodb/dist/cartodb', + text: '../vendor/assets/bower_components/requirejs-text/text', + moment: '../vendor/assets/bower_components/moment/moment', + mps: '../vendor/assets/bower_components/minpubsub/minpubsub', + + main: '../app/assets/javascripts/map/main', + wax: '../vendor/assets/javascripts/wax.g.min', + jqueryui: '../vendor/assets/javascripts/jquery-ui-1.10.4.custom.min', + backbonequeryparams: '../vendor/assets/javascripts/backbone.queryparams', + gmap: '../app/assets/javascripts/map/gmap', + backbone_cartodb: '../vendor/assets/javascripts/backbone.cartodb', + cartodblayer: '../vendor/assets/javascripts/cartodb.gmapsv3', + store: '../vendor/assets/javascripts/store', + Class: '../vendor/assets/javascripts/class', + uri: '../vendor/assets/javascripts/uri', + app: '../app/assets/javascripts/map/app', + utils: '../app/assets/javascripts/map/utils', + nsa: '../app/assets/javascripts/map/nsa', + analysis: '../app/assets/javascripts/map/analysis', + router: '../app/assets/javascripts/map/router', + mediator: '../app/assets/javascripts/map/mediator', + presenter: '../app/assets/javascripts/map/presenter', + views: '../app/assets/javascripts/map/views', + templates: '../app/assets/javascripts/map/templates', + services: '../app/assets/javascripts/map/services', + presenters: '../app/assets/javascripts/map/presenters', + models: '../app/assets/javascripts/map/models', + collections: '../app/assets/javascripts/map/collections', + itertools: '../vendor/assets/javascripts/itertools', + + spec: 'spec', + helpers: 'helpers', + jasmine: 'lib/jasmine', + jasmine_html: 'lib/jasmine-html', + jasmine_boot: 'lib/boot', + mock_ajax: 'lib/mock-ajax' }, - + shim: { jqueryui: { deps: ['jquery'], @@ -66,10 +67,10 @@ require.config({ }, uri: { exports: 'UriTemplate', - }, + }, Class: { exports: 'Class', - }, + }, app: { deps: ['mps', 'Class', 'router'], exports: 'app' @@ -93,15 +94,15 @@ require.config({ }); require([ - 'mock_ajax', - 'main', - // 'spec/nsa_spec', + 'mock_ajax', + //'main', + 'spec/nsa_spec', // 'spec/AnalysisService_spec', // 'spec/AnalysisButtonPresenter_spec', - 'spec/MapLayerService_spec', - 'spec/MapPresenter_spec', - 'spec/UMDLossLayerPresenter_spec', - 'spec/PlaceService_spec' + // 'spec/MapLayerService_spec', + // 'spec/MapPresenter_spec', + // 'spec/UMDLossLayerPresenter_spec', + // 'spec/PlaceService_spec' ], function (mock_ajax, main, nsa_spec){ console.log('Setting up specs...'); -}); \ No newline at end of file +}); diff --git a/package.json b/package.json index d897469931..727eadf542 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "globalforestwatch", - "version": "1.1.0", + "version": "0.1.0", "description": "Global Forest Watch", "main": "Gruntfile.js", "scripts": { diff --git a/vendor/assets/javascripts/backbone.js b/vendor/assets/javascripts/backbone.js deleted file mode 100644 index 24a550a0ad..0000000000 --- a/vendor/assets/javascripts/backbone.js +++ /dev/null @@ -1,1608 +0,0 @@ -// Backbone.js 1.1.2 - -// (c) 2010-2014 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors -// Backbone may be freely distributed under the MIT license. -// For all details and documentation: -// http://backbonejs.org - -(function(root, factory) { - - // Set up Backbone appropriately for the environment. Start with AMD. - if (typeof define === 'function' && define.amd) { - define(['underscore', 'jquery', 'exports'], function(_, $, exports) { - // Export global even in AMD case in case this script is loaded with - // others that may still expect a global Backbone. - root.Backbone = factory(root, exports, _, $); - }); - - // Next for Node.js or CommonJS. jQuery may not be needed as a module. - } else if (typeof exports !== 'undefined') { - var _ = require('underscore'); - factory(root, exports, _); - - // Finally, as a browser global. - } else { - root.Backbone = factory(root, {}, root._, (root.jQuery || root.Zepto || root.ender || root.$)); - } - -}(this, function(root, Backbone, _, $) { - - // Initial Setup - // ------------- - - // Save the previous value of the `Backbone` variable, so that it can be - // restored later on, if `noConflict` is used. - var previousBackbone = root.Backbone; - - // Create local references to array methods we'll want to use later. - var array = []; - var push = array.push; - var slice = array.slice; - var splice = array.splice; - - // Current version of the library. Keep in sync with `package.json`. - Backbone.VERSION = '1.1.2'; - - // For Backbone's purposes, jQuery, Zepto, Ender, or My Library (kidding) owns - // the `$` variable. - Backbone.$ = $; - - // Runs Backbone.js in *noConflict* mode, returning the `Backbone` variable - // to its previous owner. Returns a reference to this Backbone object. - Backbone.noConflict = function() { - root.Backbone = previousBackbone; - return this; - }; - - // Turn on `emulateHTTP` to support legacy HTTP servers. Setting this option - // will fake `"PATCH"`, `"PUT"` and `"DELETE"` requests via the `_method` parameter and - // set a `X-Http-Method-Override` header. - Backbone.emulateHTTP = false; - - // Turn on `emulateJSON` to support legacy servers that can't deal with direct - // `application/json` requests ... will encode the body as - // `application/x-www-form-urlencoded` instead and will send the model in a - // form param named `model`. - Backbone.emulateJSON = false; - - // Backbone.Events - // --------------- - - // A module that can be mixed in to *any object* in order to provide it with - // custom events. You may bind with `on` or remove with `off` callback - // functions to an event; `trigger`-ing an event fires all callbacks in - // succession. - // - // var object = {}; - // _.extend(object, Backbone.Events); - // object.on('expand', function(){ alert('expanded'); }); - // object.trigger('expand'); - // - var Events = Backbone.Events = { - - // Bind an event to a `callback` function. Passing `"all"` will bind - // the callback to all events fired. - on: function(name, callback, context) { - if (!eventsApi(this, 'on', name, [callback, context]) || !callback) return this; - this._events || (this._events = {}); - var events = this._events[name] || (this._events[name] = []); - events.push({callback: callback, context: context, ctx: context || this}); - return this; - }, - - // Bind an event to only be triggered a single time. After the first time - // the callback is invoked, it will be removed. - once: function(name, callback, context) { - if (!eventsApi(this, 'once', name, [callback, context]) || !callback) return this; - var self = this; - var once = _.once(function() { - self.off(name, once); - callback.apply(this, arguments); - }); - once._callback = callback; - return this.on(name, once, context); - }, - - // Remove one or many callbacks. If `context` is null, removes all - // callbacks with that function. If `callback` is null, removes all - // callbacks for the event. If `name` is null, removes all bound - // callbacks for all events. - off: function(name, callback, context) { - var retain, ev, events, names, i, l, j, k; - if (!this._events || !eventsApi(this, 'off', name, [callback, context])) return this; - if (!name && !callback && !context) { - this._events = void 0; - return this; - } - names = name ? [name] : _.keys(this._events); - for (i = 0, l = names.length; i < l; i++) { - name = names[i]; - if (events = this._events[name]) { - this._events[name] = retain = []; - if (callback || context) { - for (j = 0, k = events.length; j < k; j++) { - ev = events[j]; - if ((callback && callback !== ev.callback && callback !== ev.callback._callback) || - (context && context !== ev.context)) { - retain.push(ev); - } - } - } - if (!retain.length) delete this._events[name]; - } - } - - return this; - }, - - // Trigger one or many events, firing all bound callbacks. Callbacks are - // passed the same arguments as `trigger` is, apart from the event name - // (unless you're listening on `"all"`, which will cause your callback to - // receive the true name of the event as the first argument). - trigger: function(name) { - if (!this._events) return this; - var args = slice.call(arguments, 1); - if (!eventsApi(this, 'trigger', name, args)) return this; - var events = this._events[name]; - var allEvents = this._events.all; - if (events) triggerEvents(events, args); - if (allEvents) triggerEvents(allEvents, arguments); - return this; - }, - - // Tell this object to stop listening to either specific events ... or - // to every object it's currently listening to. - stopListening: function(obj, name, callback) { - var listeningTo = this._listeningTo; - if (!listeningTo) return this; - var remove = !name && !callback; - if (!callback && typeof name === 'object') callback = this; - if (obj) (listeningTo = {})[obj._listenId] = obj; - for (var id in listeningTo) { - obj = listeningTo[id]; - obj.off(name, callback, this); - if (remove || _.isEmpty(obj._events)) delete this._listeningTo[id]; - } - return this; - } - - }; - - // Regular expression used to split event strings. - var eventSplitter = /\s+/; - - // Implement fancy features of the Events API such as multiple event - // names `"change blur"` and jQuery-style event maps `{change: action}` - // in terms of the existing API. - var eventsApi = function(obj, action, name, rest) { - if (!name) return true; - - // Handle event maps. - if (typeof name === 'object') { - for (var key in name) { - obj[action].apply(obj, [key, name[key]].concat(rest)); - } - return false; - } - - // Handle space separated event names. - if (eventSplitter.test(name)) { - var names = name.split(eventSplitter); - for (var i = 0, l = names.length; i < l; i++) { - obj[action].apply(obj, [names[i]].concat(rest)); - } - return false; - } - - return true; - }; - - // A difficult-to-believe, but optimized internal dispatch function for - // triggering events. Tries to keep the usual cases speedy (most internal - // Backbone events have 3 arguments). - var triggerEvents = function(events, args) { - var ev, i = -1, l = events.length, a1 = args[0], a2 = args[1], a3 = args[2]; - switch (args.length) { - case 0: while (++i < l) (ev = events[i]).callback.call(ev.ctx); return; - case 1: while (++i < l) (ev = events[i]).callback.call(ev.ctx, a1); return; - case 2: while (++i < l) (ev = events[i]).callback.call(ev.ctx, a1, a2); return; - case 3: while (++i < l) (ev = events[i]).callback.call(ev.ctx, a1, a2, a3); return; - default: while (++i < l) (ev = events[i]).callback.apply(ev.ctx, args); return; - } - }; - - var listenMethods = {listenTo: 'on', listenToOnce: 'once'}; - - // Inversion-of-control versions of `on` and `once`. Tell *this* object to - // listen to an event in another object ... keeping track of what it's - // listening to. - _.each(listenMethods, function(implementation, method) { - Events[method] = function(obj, name, callback) { - var listeningTo = this._listeningTo || (this._listeningTo = {}); - var id = obj._listenId || (obj._listenId = _.uniqueId('l')); - listeningTo[id] = obj; - if (!callback && typeof name === 'object') callback = this; - obj[implementation](name, callback, this); - return this; - }; - }); - - // Aliases for backwards compatibility. - Events.bind = Events.on; - Events.unbind = Events.off; - - // Allow the `Backbone` object to serve as a global event bus, for folks who - // want global "pubsub" in a convenient place. - _.extend(Backbone, Events); - - // Backbone.Model - // -------------- - - // Backbone **Models** are the basic data object in the framework -- - // frequently representing a row in a table in a database on your server. - // A discrete chunk of data and a bunch of useful, related methods for - // performing computations and transformations on that data. - - // Create a new model with the specified attributes. A client id (`cid`) - // is automatically generated and assigned for you. - var Model = Backbone.Model = function(attributes, options) { - var attrs = attributes || {}; - options || (options = {}); - this.cid = _.uniqueId('c'); - this.attributes = {}; - if (options.collection) this.collection = options.collection; - if (options.parse) attrs = this.parse(attrs, options) || {}; - attrs = _.defaults({}, attrs, _.result(this, 'defaults')); - this.set(attrs, options); - this.changed = {}; - this.initialize.apply(this, arguments); - }; - - // Attach all inheritable methods to the Model prototype. - _.extend(Model.prototype, Events, { - - // A hash of attributes whose current and previous value differ. - changed: null, - - // The value returned during the last failed validation. - validationError: null, - - // The default name for the JSON `id` attribute is `"id"`. MongoDB and - // CouchDB users may want to set this to `"_id"`. - idAttribute: 'id', - - // Initialize is an empty function by default. Override it with your own - // initialization logic. - initialize: function(){}, - - // Return a copy of the model's `attributes` object. - toJSON: function(options) { - return _.clone(this.attributes); - }, - - // Proxy `Backbone.sync` by default -- but override this if you need - // custom syncing semantics for *this* particular model. - sync: function() { - return Backbone.sync.apply(this, arguments); - }, - - // Get the value of an attribute. - get: function(attr) { - return this.attributes[attr]; - }, - - // Get the HTML-escaped value of an attribute. - escape: function(attr) { - return _.escape(this.get(attr)); - }, - - // Returns `true` if the attribute contains a value that is not null - // or undefined. - has: function(attr) { - return this.get(attr) != null; - }, - - // Set a hash of model attributes on the object, firing `"change"`. This is - // the core primitive operation of a model, updating the data and notifying - // anyone who needs to know about the change in state. The heart of the beast. - set: function(key, val, options) { - var attr, attrs, unset, changes, silent, changing, prev, current; - if (key == null) return this; - - // Handle both `"key", value` and `{key: value}` -style arguments. - if (typeof key === 'object') { - attrs = key; - options = val; - } else { - (attrs = {})[key] = val; - } - - options || (options = {}); - - // Run validation. - if (!this._validate(attrs, options)) return false; - - // Extract attributes and options. - unset = options.unset; - silent = options.silent; - changes = []; - changing = this._changing; - this._changing = true; - - if (!changing) { - this._previousAttributes = _.clone(this.attributes); - this.changed = {}; - } - current = this.attributes, prev = this._previousAttributes; - - // Check for changes of `id`. - if (this.idAttribute in attrs) this.id = attrs[this.idAttribute]; - - // For each `set` attribute, update or delete the current value. - for (attr in attrs) { - val = attrs[attr]; - if (!_.isEqual(current[attr], val)) changes.push(attr); - if (!_.isEqual(prev[attr], val)) { - this.changed[attr] = val; - } else { - delete this.changed[attr]; - } - unset ? delete current[attr] : current[attr] = val; - } - - // Trigger all relevant attribute changes. - if (!silent) { - if (changes.length) this._pending = options; - for (var i = 0, l = changes.length; i < l; i++) { - this.trigger('change:' + changes[i], this, current[changes[i]], options); - } - } - - // You might be wondering why there's a `while` loop here. Changes can - // be recursively nested within `"change"` events. - if (changing) return this; - if (!silent) { - while (this._pending) { - options = this._pending; - this._pending = false; - this.trigger('change', this, options); - } - } - this._pending = false; - this._changing = false; - return this; - }, - - // Remove an attribute from the model, firing `"change"`. `unset` is a noop - // if the attribute doesn't exist. - unset: function(attr, options) { - return this.set(attr, void 0, _.extend({}, options, {unset: true})); - }, - - // Clear all attributes on the model, firing `"change"`. - clear: function(options) { - var attrs = {}; - for (var key in this.attributes) attrs[key] = void 0; - return this.set(attrs, _.extend({}, options, {unset: true})); - }, - - // Determine if the model has changed since the last `"change"` event. - // If you specify an attribute name, determine if that attribute has changed. - hasChanged: function(attr) { - if (attr == null) return !_.isEmpty(this.changed); - return _.has(this.changed, attr); - }, - - // Return an object containing all the attributes that have changed, or - // false if there are no changed attributes. Useful for determining what - // parts of a view need to be updated and/or what attributes need to be - // persisted to the server. Unset attributes will be set to undefined. - // You can also pass an attributes object to diff against the model, - // determining if there *would be* a change. - changedAttributes: function(diff) { - if (!diff) return this.hasChanged() ? _.clone(this.changed) : false; - var val, changed = false; - var old = this._changing ? this._previousAttributes : this.attributes; - for (var attr in diff) { - if (_.isEqual(old[attr], (val = diff[attr]))) continue; - (changed || (changed = {}))[attr] = val; - } - return changed; - }, - - // Get the previous value of an attribute, recorded at the time the last - // `"change"` event was fired. - previous: function(attr) { - if (attr == null || !this._previousAttributes) return null; - return this._previousAttributes[attr]; - }, - - // Get all of the attributes of the model at the time of the previous - // `"change"` event. - previousAttributes: function() { - return _.clone(this._previousAttributes); - }, - - // Fetch the model from the server. If the server's representation of the - // model differs from its current attributes, they will be overridden, - // triggering a `"change"` event. - fetch: function(options) { - options = options ? _.clone(options) : {}; - if (options.parse === void 0) options.parse = true; - var model = this; - var success = options.success; - options.success = function(resp) { - if (!model.set(model.parse(resp, options), options)) return false; - if (success) success(model, resp, options); - model.trigger('sync', model, resp, options); - }; - wrapError(this, options); - return this.sync('read', this, options); - }, - - // Set a hash of model attributes, and sync the model to the server. - // If the server returns an attributes hash that differs, the model's - // state will be `set` again. - save: function(key, val, options) { - var attrs, method, xhr, attributes = this.attributes; - - // Handle both `"key", value` and `{key: value}` -style arguments. - if (key == null || typeof key === 'object') { - attrs = key; - options = val; - } else { - (attrs = {})[key] = val; - } - - options = _.extend({validate: true}, options); - - // If we're not waiting and attributes exist, save acts as - // `set(attr).save(null, opts)` with validation. Otherwise, check if - // the model will be valid when the attributes, if any, are set. - if (attrs && !options.wait) { - if (!this.set(attrs, options)) return false; - } else { - if (!this._validate(attrs, options)) return false; - } - - // Set temporary attributes if `{wait: true}`. - if (attrs && options.wait) { - this.attributes = _.extend({}, attributes, attrs); - } - - // After a successful server-side save, the client is (optionally) - // updated with the server-side state. - if (options.parse === void 0) options.parse = true; - var model = this; - var success = options.success; - options.success = function(resp) { - // Ensure attributes are restored during synchronous saves. - model.attributes = attributes; - var serverAttrs = model.parse(resp, options); - if (options.wait) serverAttrs = _.extend(attrs || {}, serverAttrs); - if (_.isObject(serverAttrs) && !model.set(serverAttrs, options)) { - return false; - } - if (success) success(model, resp, options); - model.trigger('sync', model, resp, options); - }; - wrapError(this, options); - - method = this.isNew() ? 'create' : (options.patch ? 'patch' : 'update'); - if (method === 'patch') options.attrs = attrs; - xhr = this.sync(method, this, options); - - // Restore attributes. - if (attrs && options.wait) this.attributes = attributes; - - return xhr; - }, - - // Destroy this model on the server if it was already persisted. - // Optimistically removes the model from its collection, if it has one. - // If `wait: true` is passed, waits for the server to respond before removal. - destroy: function(options) { - options = options ? _.clone(options) : {}; - var model = this; - var success = options.success; - - var destroy = function() { - model.trigger('destroy', model, model.collection, options); - }; - - options.success = function(resp) { - if (options.wait || model.isNew()) destroy(); - if (success) success(model, resp, options); - if (!model.isNew()) model.trigger('sync', model, resp, options); - }; - - if (this.isNew()) { - options.success(); - return false; - } - wrapError(this, options); - - var xhr = this.sync('delete', this, options); - if (!options.wait) destroy(); - return xhr; - }, - - // Default URL for the model's representation on the server -- if you're - // using Backbone's restful methods, override this to change the endpoint - // that will be called. - url: function() { - var base = - _.result(this, 'urlRoot') || - _.result(this.collection, 'url') || - urlError(); - if (this.isNew()) return base; - return base.replace(/([^\/])$/, '$1/') + encodeURIComponent(this.id); - }, - - // **parse** converts a response into the hash of attributes to be `set` on - // the model. The default implementation is just to pass the response along. - parse: function(resp, options) { - return resp; - }, - - // Create a new model with identical attributes to this one. - clone: function() { - return new this.constructor(this.attributes); - }, - - // A model is new if it has never been saved to the server, and lacks an id. - isNew: function() { - return !this.has(this.idAttribute); - }, - - // Check if the model is currently in a valid state. - isValid: function(options) { - return this._validate({}, _.extend(options || {}, { validate: true })); - }, - - // Run validation against the next complete set of model attributes, - // returning `true` if all is well. Otherwise, fire an `"invalid"` event. - _validate: function(attrs, options) { - if (!options.validate || !this.validate) return true; - attrs = _.extend({}, this.attributes, attrs); - var error = this.validationError = this.validate(attrs, options) || null; - if (!error) return true; - this.trigger('invalid', this, error, _.extend(options, {validationError: error})); - return false; - } - - }); - - // Underscore methods that we want to implement on the Model. - var modelMethods = ['keys', 'values', 'pairs', 'invert', 'pick', 'omit']; - - // Mix in each Underscore method as a proxy to `Model#attributes`. - _.each(modelMethods, function(method) { - Model.prototype[method] = function() { - var args = slice.call(arguments); - args.unshift(this.attributes); - return _[method].apply(_, args); - }; - }); - - // Backbone.Collection - // ------------------- - - // If models tend to represent a single row of data, a Backbone Collection is - // more analagous to a table full of data ... or a small slice or page of that - // table, or a collection of rows that belong together for a particular reason - // -- all of the messages in this particular folder, all of the documents - // belonging to this particular author, and so on. Collections maintain - // indexes of their models, both in order, and for lookup by `id`. - - // Create a new **Collection**, perhaps to contain a specific type of `model`. - // If a `comparator` is specified, the Collection will maintain - // its models in sort order, as they're added and removed. - var Collection = Backbone.Collection = function(models, options) { - options || (options = {}); - if (options.model) this.model = options.model; - if (options.comparator !== void 0) this.comparator = options.comparator; - this._reset(); - this.initialize.apply(this, arguments); - if (models) this.reset(models, _.extend({silent: true}, options)); - }; - - // Default options for `Collection#set`. - var setOptions = {add: true, remove: true, merge: true}; - var addOptions = {add: true, remove: false}; - - // Define the Collection's inheritable methods. - _.extend(Collection.prototype, Events, { - - // The default model for a collection is just a **Backbone.Model**. - // This should be overridden in most cases. - model: Model, - - // Initialize is an empty function by default. Override it with your own - // initialization logic. - initialize: function(){}, - - // The JSON representation of a Collection is an array of the - // models' attributes. - toJSON: function(options) { - return this.map(function(model){ return model.toJSON(options); }); - }, - - // Proxy `Backbone.sync` by default. - sync: function() { - return Backbone.sync.apply(this, arguments); - }, - - // Add a model, or list of models to the set. - add: function(models, options) { - return this.set(models, _.extend({merge: false}, options, addOptions)); - }, - - // Remove a model, or a list of models from the set. - remove: function(models, options) { - var singular = !_.isArray(models); - models = singular ? [models] : _.clone(models); - options || (options = {}); - var i, l, index, model; - for (i = 0, l = models.length; i < l; i++) { - model = models[i] = this.get(models[i]); - if (!model) continue; - delete this._byId[model.id]; - delete this._byId[model.cid]; - index = this.indexOf(model); - this.models.splice(index, 1); - this.length--; - if (!options.silent) { - options.index = index; - model.trigger('remove', model, this, options); - } - this._removeReference(model, options); - } - return singular ? models[0] : models; - }, - - // Update a collection by `set`-ing a new list of models, adding new ones, - // removing models that are no longer present, and merging models that - // already exist in the collection, as necessary. Similar to **Model#set**, - // the core operation for updating the data contained by the collection. - set: function(models, options) { - options = _.defaults({}, options, setOptions); - if (options.parse) models = this.parse(models, options); - var singular = !_.isArray(models); - models = singular ? (models ? [models] : []) : _.clone(models); - var i, l, id, model, attrs, existing, sort; - var at = options.at; - var targetModel = this.model; - var sortable = this.comparator && (at == null) && options.sort !== false; - var sortAttr = _.isString(this.comparator) ? this.comparator : null; - var toAdd = [], toRemove = [], modelMap = {}; - var add = options.add, merge = options.merge, remove = options.remove; - var order = !sortable && add && remove ? [] : false; - - // Turn bare objects into model references, and prevent invalid models - // from being added. - for (i = 0, l = models.length; i < l; i++) { - attrs = models[i] || {}; - if (attrs instanceof Model) { - id = model = attrs; - } else { - id = attrs[targetModel.prototype.idAttribute || 'id']; - } - - // If a duplicate is found, prevent it from being added and - // optionally merge it into the existing model. - if (existing = this.get(id)) { - if (remove) modelMap[existing.cid] = true; - if (merge) { - attrs = attrs === model ? model.attributes : attrs; - if (options.parse) attrs = existing.parse(attrs, options); - existing.set(attrs, options); - if (sortable && !sort && existing.hasChanged(sortAttr)) sort = true; - } - models[i] = existing; - - // If this is a new, valid model, push it to the `toAdd` list. - } else if (add) { - model = models[i] = this._prepareModel(attrs, options); - if (!model) continue; - toAdd.push(model); - this._addReference(model, options); - } - - // Do not add multiple models with the same `id`. - model = existing || model; - if (order && (model.isNew() || !modelMap[model.id])) order.push(model); - modelMap[model.id] = true; - } - - // Remove nonexistent models if appropriate. - if (remove) { - for (i = 0, l = this.length; i < l; ++i) { - if (!modelMap[(model = this.models[i]).cid]) toRemove.push(model); - } - if (toRemove.length) this.remove(toRemove, options); - } - - // See if sorting is needed, update `length` and splice in new models. - if (toAdd.length || (order && order.length)) { - if (sortable) sort = true; - this.length += toAdd.length; - if (at != null) { - for (i = 0, l = toAdd.length; i < l; i++) { - this.models.splice(at + i, 0, toAdd[i]); - } - } else { - if (order) this.models.length = 0; - var orderedModels = order || toAdd; - for (i = 0, l = orderedModels.length; i < l; i++) { - this.models.push(orderedModels[i]); - } - } - } - - // Silently sort the collection if appropriate. - if (sort) this.sort({silent: true}); - - // Unless silenced, it's time to fire all appropriate add/sort events. - if (!options.silent) { - for (i = 0, l = toAdd.length; i < l; i++) { - (model = toAdd[i]).trigger('add', model, this, options); - } - if (sort || (order && order.length)) this.trigger('sort', this, options); - } - - // Return the added (or merged) model (or models). - return singular ? models[0] : models; - }, - - // When you have more items than you want to add or remove individually, - // you can reset the entire set with a new list of models, without firing - // any granular `add` or `remove` events. Fires `reset` when finished. - // Useful for bulk operations and optimizations. - reset: function(models, options) { - options || (options = {}); - for (var i = 0, l = this.models.length; i < l; i++) { - this._removeReference(this.models[i], options); - } - options.previousModels = this.models; - this._reset(); - models = this.add(models, _.extend({silent: true}, options)); - if (!options.silent) this.trigger('reset', this, options); - return models; - }, - - // Add a model to the end of the collection. - push: function(model, options) { - return this.add(model, _.extend({at: this.length}, options)); - }, - - // Remove a model from the end of the collection. - pop: function(options) { - var model = this.at(this.length - 1); - this.remove(model, options); - return model; - }, - - // Add a model to the beginning of the collection. - unshift: function(model, options) { - return this.add(model, _.extend({at: 0}, options)); - }, - - // Remove a model from the beginning of the collection. - shift: function(options) { - var model = this.at(0); - this.remove(model, options); - return model; - }, - - // Slice out a sub-array of models from the collection. - slice: function() { - return slice.apply(this.models, arguments); - }, - - // Get a model from the set by id. - get: function(obj) { - if (obj == null) return void 0; - return this._byId[obj] || this._byId[obj.id] || this._byId[obj.cid]; - }, - - // Get the model at the given index. - at: function(index) { - return this.models[index]; - }, - - // Return models with matching attributes. Useful for simple cases of - // `filter`. - where: function(attrs, first) { - if (_.isEmpty(attrs)) return first ? void 0 : []; - return this[first ? 'find' : 'filter'](function(model) { - for (var key in attrs) { - if (attrs[key] !== model.get(key)) return false; - } - return true; - }); - }, - - // Return the first model with matching attributes. Useful for simple cases - // of `find`. - findWhere: function(attrs) { - return this.where(attrs, true); - }, - - // Force the collection to re-sort itself. You don't need to call this under - // normal circumstances, as the set will maintain sort order as each item - // is added. - sort: function(options) { - if (!this.comparator) throw new Error('Cannot sort a set without a comparator'); - options || (options = {}); - - // Run sort based on type of `comparator`. - if (_.isString(this.comparator) || this.comparator.length === 1) { - this.models = this.sortBy(this.comparator, this); - } else { - this.models.sort(_.bind(this.comparator, this)); - } - - if (!options.silent) this.trigger('sort', this, options); - return this; - }, - - // Pluck an attribute from each model in the collection. - pluck: function(attr) { - return _.invoke(this.models, 'get', attr); - }, - - // Fetch the default set of models for this collection, resetting the - // collection when they arrive. If `reset: true` is passed, the response - // data will be passed through the `reset` method instead of `set`. - fetch: function(options) { - options = options ? _.clone(options) : {}; - if (options.parse === void 0) options.parse = true; - var success = options.success; - var collection = this; - options.success = function(resp) { - var method = options.reset ? 'reset' : 'set'; - collection[method](resp, options); - if (success) success(collection, resp, options); - collection.trigger('sync', collection, resp, options); - }; - wrapError(this, options); - return this.sync('read', this, options); - }, - - // Create a new instance of a model in this collection. Add the model to the - // collection immediately, unless `wait: true` is passed, in which case we - // wait for the server to agree. - create: function(model, options) { - options = options ? _.clone(options) : {}; - if (!(model = this._prepareModel(model, options))) return false; - if (!options.wait) this.add(model, options); - var collection = this; - var success = options.success; - options.success = function(model, resp) { - if (options.wait) collection.add(model, options); - if (success) success(model, resp, options); - }; - model.save(null, options); - return model; - }, - - // **parse** converts a response into a list of models to be added to the - // collection. The default implementation is just to pass it through. - parse: function(resp, options) { - return resp; - }, - - // Create a new collection with an identical list of models as this one. - clone: function() { - return new this.constructor(this.models); - }, - - // Private method to reset all internal state. Called when the collection - // is first initialized or reset. - _reset: function() { - this.length = 0; - this.models = []; - this._byId = {}; - }, - - // Prepare a hash of attributes (or other model) to be added to this - // collection. - _prepareModel: function(attrs, options) { - if (attrs instanceof Model) return attrs; - options = options ? _.clone(options) : {}; - options.collection = this; - var model = new this.model(attrs, options); - if (!model.validationError) return model; - this.trigger('invalid', this, model.validationError, options); - return false; - }, - - // Internal method to create a model's ties to a collection. - _addReference: function(model, options) { - this._byId[model.cid] = model; - if (model.id != null) this._byId[model.id] = model; - if (!model.collection) model.collection = this; - model.on('all', this._onModelEvent, this); - }, - - // Internal method to sever a model's ties to a collection. - _removeReference: function(model, options) { - if (this === model.collection) delete model.collection; - model.off('all', this._onModelEvent, this); - }, - - // Internal method called every time a model in the set fires an event. - // Sets need to update their indexes when models change ids. All other - // events simply proxy through. "add" and "remove" events that originate - // in other collections are ignored. - _onModelEvent: function(event, model, collection, options) { - if ((event === 'add' || event === 'remove') && collection !== this) return; - if (event === 'destroy') this.remove(model, options); - if (model && event === 'change:' + model.idAttribute) { - delete this._byId[model.previous(model.idAttribute)]; - if (model.id != null) this._byId[model.id] = model; - } - this.trigger.apply(this, arguments); - } - - }); - - // Underscore methods that we want to implement on the Collection. - // 90% of the core usefulness of Backbone Collections is actually implemented - // right here: - var methods = ['forEach', 'each', 'map', 'collect', 'reduce', 'foldl', - 'inject', 'reduceRight', 'foldr', 'find', 'detect', 'filter', 'select', - 'reject', 'every', 'all', 'some', 'any', 'include', 'contains', 'invoke', - 'max', 'min', 'toArray', 'size', 'first', 'head', 'take', 'initial', 'rest', - 'tail', 'drop', 'last', 'without', 'difference', 'indexOf', 'shuffle', - 'lastIndexOf', 'isEmpty', 'chain', 'sample']; - - // Mix in each Underscore method as a proxy to `Collection#models`. - _.each(methods, function(method) { - Collection.prototype[method] = function() { - var args = slice.call(arguments); - args.unshift(this.models); - return _[method].apply(_, args); - }; - }); - - // Underscore methods that take a property name as an argument. - var attributeMethods = ['groupBy', 'countBy', 'sortBy', 'indexBy']; - - // Use attributes instead of properties. - _.each(attributeMethods, function(method) { - Collection.prototype[method] = function(value, context) { - var iterator = _.isFunction(value) ? value : function(model) { - return model.get(value); - }; - return _[method](this.models, iterator, context); - }; - }); - - // Backbone.View - // ------------- - - // Backbone Views are almost more convention than they are actual code. A View - // is simply a JavaScript object that represents a logical chunk of UI in the - // DOM. This might be a single item, an entire list, a sidebar or panel, or - // even the surrounding frame which wraps your whole app. Defining a chunk of - // UI as a **View** allows you to define your DOM events declaratively, without - // having to worry about render order ... and makes it easy for the view to - // react to specific changes in the state of your models. - - // Creating a Backbone.View creates its initial element outside of the DOM, - // if an existing element is not provided... - var View = Backbone.View = function(options) { - this.cid = _.uniqueId('view'); - options || (options = {}); - _.extend(this, _.pick(options, viewOptions)); - this._ensureElement(); - this.initialize.apply(this, arguments); - this.delegateEvents(); - }; - - // Cached regex to split keys for `delegate`. - var delegateEventSplitter = /^(\S+)\s*(.*)$/; - - // List of view options to be merged as properties. - var viewOptions = ['model', 'collection', 'el', 'id', 'attributes', 'className', 'tagName', 'events']; - - // Set up all inheritable **Backbone.View** properties and methods. - _.extend(View.prototype, Events, { - - // The default `tagName` of a View's element is `"div"`. - tagName: 'div', - - // jQuery delegate for element lookup, scoped to DOM elements within the - // current view. This should be preferred to global lookups where possible. - $: function(selector) { - return this.$el.find(selector); - }, - - // Initialize is an empty function by default. Override it with your own - // initialization logic. - initialize: function(){}, - - // **render** is the core function that your view should override, in order - // to populate its element (`this.el`), with the appropriate HTML. The - // convention is for **render** to always return `this`. - render: function() { - return this; - }, - - // Remove this view by taking the element out of the DOM, and removing any - // applicable Backbone.Events listeners. - remove: function() { - this.$el.remove(); - this.stopListening(); - return this; - }, - - // Change the view's element (`this.el` property), including event - // re-delegation. - setElement: function(element, delegate) { - if (this.$el) this.undelegateEvents(); - this.$el = element instanceof Backbone.$ ? element : Backbone.$(element); - this.el = this.$el[0]; - if (delegate !== false) this.delegateEvents(); - return this; - }, - - // Set callbacks, where `this.events` is a hash of - // - // *{"event selector": "callback"}* - // - // { - // 'mousedown .title': 'edit', - // 'click .button': 'save', - // 'click .open': function(e) { ... } - // } - // - // pairs. Callbacks will be bound to the view, with `this` set properly. - // Uses event delegation for efficiency. - // Omitting the selector binds the event to `this.el`. - // This only works for delegate-able events: not `focus`, `blur`, and - // not `change`, `submit`, and `reset` in Internet Explorer. - delegateEvents: function(events) { - if (!(events || (events = _.result(this, 'events')))) return this; - this.undelegateEvents(); - for (var key in events) { - var method = events[key]; - if (!_.isFunction(method)) method = this[events[key]]; - if (!method) continue; - - var match = key.match(delegateEventSplitter); - var eventName = match[1], selector = match[2]; - method = _.bind(method, this); - eventName += '.delegateEvents' + this.cid; - if (selector === '') { - this.$el.on(eventName, method); - } else { - this.$el.on(eventName, selector, method); - } - } - return this; - }, - - // Clears all callbacks previously bound to the view with `delegateEvents`. - // You usually don't need to use this, but may wish to if you have multiple - // Backbone views attached to the same DOM element. - undelegateEvents: function() { - this.$el.off('.delegateEvents' + this.cid); - return this; - }, - - // Ensure that the View has a DOM element to render into. - // If `this.el` is a string, pass it through `$()`, take the first - // matching element, and re-assign it to `el`. Otherwise, create - // an element from the `id`, `className` and `tagName` properties. - _ensureElement: function() { - if (!this.el) { - var attrs = _.extend({}, _.result(this, 'attributes')); - if (this.id) attrs.id = _.result(this, 'id'); - if (this.className) attrs['class'] = _.result(this, 'className'); - var $el = Backbone.$('<' + _.result(this, 'tagName') + '>').attr(attrs); - this.setElement($el, false); - } else { - this.setElement(_.result(this, 'el'), false); - } - } - - }); - - // Backbone.sync - // ------------- - - // Override this function to change the manner in which Backbone persists - // models to the server. You will be passed the type of request, and the - // model in question. By default, makes a RESTful Ajax request - // to the model's `url()`. Some possible customizations could be: - // - // * Use `setTimeout` to batch rapid-fire updates into a single request. - // * Send up the models as XML instead of JSON. - // * Persist models via WebSockets instead of Ajax. - // - // Turn on `Backbone.emulateHTTP` in order to send `PUT` and `DELETE` requests - // as `POST`, with a `_method` parameter containing the true HTTP method, - // as well as all requests with the body as `application/x-www-form-urlencoded` - // instead of `application/json` with the model in a param named `model`. - // Useful when interfacing with server-side languages like **PHP** that make - // it difficult to read the body of `PUT` requests. - Backbone.sync = function(method, model, options) { - var type = methodMap[method]; - - // Default options, unless specified. - _.defaults(options || (options = {}), { - emulateHTTP: Backbone.emulateHTTP, - emulateJSON: Backbone.emulateJSON - }); - - // Default JSON-request options. - var params = {type: type, dataType: 'json'}; - - // Ensure that we have a URL. - if (!options.url) { - params.url = _.result(model, 'url') || urlError(); - } - - // Ensure that we have the appropriate request data. - if (options.data == null && model && (method === 'create' || method === 'update' || method === 'patch')) { - params.contentType = 'application/json'; - params.data = JSON.stringify(options.attrs || model.toJSON(options)); - } - - // For older servers, emulate JSON by encoding the request into an HTML-form. - if (options.emulateJSON) { - params.contentType = 'application/x-www-form-urlencoded'; - params.data = params.data ? {model: params.data} : {}; - } - - // For older servers, emulate HTTP by mimicking the HTTP method with `_method` - // And an `X-HTTP-Method-Override` header. - if (options.emulateHTTP && (type === 'PUT' || type === 'DELETE' || type === 'PATCH')) { - params.type = 'POST'; - if (options.emulateJSON) params.data._method = type; - var beforeSend = options.beforeSend; - options.beforeSend = function(xhr) { - xhr.setRequestHeader('X-HTTP-Method-Override', type); - if (beforeSend) return beforeSend.apply(this, arguments); - }; - } - - // Don't process data on a non-GET request. - if (params.type !== 'GET' && !options.emulateJSON) { - params.processData = false; - } - - // If we're sending a `PATCH` request, and we're in an old Internet Explorer - // that still has ActiveX enabled by default, override jQuery to use that - // for XHR instead. Remove this line when jQuery supports `PATCH` on IE8. - if (params.type === 'PATCH' && noXhrPatch) { - params.xhr = function() { - return new ActiveXObject("Microsoft.XMLHTTP"); - }; - } - - // Make the request, allowing the user to override any Ajax options. - var xhr = options.xhr = Backbone.ajax(_.extend(params, options)); - model.trigger('request', model, xhr, options); - return xhr; - }; - - var noXhrPatch = - typeof window !== 'undefined' && !!window.ActiveXObject && - !(window.XMLHttpRequest && (new XMLHttpRequest).dispatchEvent); - - // Map from CRUD to HTTP for our default `Backbone.sync` implementation. - var methodMap = { - 'create': 'POST', - 'update': 'PUT', - 'patch': 'PATCH', - 'delete': 'DELETE', - 'read': 'GET' - }; - - // Set the default implementation of `Backbone.ajax` to proxy through to `$`. - // Override this if you'd like to use a different library. - Backbone.ajax = function() { - return Backbone.$.ajax.apply(Backbone.$, arguments); - }; - - // Backbone.Router - // --------------- - - // Routers map faux-URLs to actions, and fire events when routes are - // matched. Creating a new one sets its `routes` hash, if not set statically. - var Router = Backbone.Router = function(options) { - options || (options = {}); - if (options.routes) this.routes = options.routes; - this._bindRoutes(); - this.initialize.apply(this, arguments); - }; - - // Cached regular expressions for matching named param parts and splatted - // parts of route strings. - var optionalParam = /\((.*?)\)/g; - var namedParam = /(\(\?)?:\w+/g; - var splatParam = /\*\w+/g; - var escapeRegExp = /[\-{}\[\]+?.,\\\^$|#\s]/g; - - // Set up all inheritable **Backbone.Router** properties and methods. - _.extend(Router.prototype, Events, { - - // Initialize is an empty function by default. Override it with your own - // initialization logic. - initialize: function(){}, - - // Manually bind a single named route to a callback. For example: - // - // this.route('search/:query/p:num', 'search', function(query, num) { - // ... - // }); - // - route: function(route, name, callback) { - if (!_.isRegExp(route)) route = this._routeToRegExp(route); - if (_.isFunction(name)) { - callback = name; - name = ''; - } - if (!callback) callback = this[name]; - var router = this; - Backbone.history.route(route, function(fragment) { - var args = router._extractParameters(route, fragment); - router.execute(callback, args); - router.trigger.apply(router, ['route:' + name].concat(args)); - router.trigger('route', name, args); - Backbone.history.trigger('route', router, name, args); - }); - return this; - }, - - // Execute a route handler with the provided parameters. This is an - // excellent place to do pre-route setup or post-route cleanup. - execute: function(callback, args) { - if (callback) callback.apply(this, args); - }, - - // Simple proxy to `Backbone.history` to save a fragment into the history. - navigate: function(fragment, options) { - Backbone.history.navigate(fragment, options); - return this; - }, - - // Bind all defined routes to `Backbone.history`. We have to reverse the - // order of the routes here to support behavior where the most general - // routes can be defined at the bottom of the route map. - _bindRoutes: function() { - if (!this.routes) return; - this.routes = _.result(this, 'routes'); - var route, routes = _.keys(this.routes); - while ((route = routes.pop()) != null) { - this.route(route, this.routes[route]); - } - }, - - // Convert a route string into a regular expression, suitable for matching - // against the current location hash. - _routeToRegExp: function(route) { - route = route.replace(escapeRegExp, '\\$&') - .replace(optionalParam, '(?:$1)?') - .replace(namedParam, function(match, optional) { - return optional ? match : '([^/?]+)'; - }) - .replace(splatParam, '([^?]*?)'); - return new RegExp('^' + route + '(?:\\?([\\s\\S]*))?$'); - }, - - // Given a route, and a URL fragment that it matches, return the array of - // extracted decoded parameters. Empty or unmatched parameters will be - // treated as `null` to normalize cross-browser behavior. - _extractParameters: function(route, fragment) { - var params = route.exec(fragment).slice(1); - return _.map(params, function(param, i) { - // Don't decode the search params. - if (i === params.length - 1) return param || null; - return param ? decodeURIComponent(param) : null; - }); - } - - }); - - // Backbone.History - // ---------------- - - // Handles cross-browser history management, based on either - // [pushState](http://diveintohtml5.info/history.html) and real URLs, or - // [onhashchange](https://developer.mozilla.org/en-US/docs/DOM/window.onhashchange) - // and URL fragments. If the browser supports neither (old IE, natch), - // falls back to polling. - var History = Backbone.History = function() { - this.handlers = []; - _.bindAll(this, 'checkUrl'); - - // Ensure that `History` can be used outside of the browser. - if (typeof window !== 'undefined') { - this.location = window.location; - this.history = window.history; - } - }; - - // Cached regex for stripping a leading hash/slash and trailing space. - var routeStripper = /^[#\/]|\s+$/g; - - // Cached regex for stripping leading and trailing slashes. - var rootStripper = /^\/+|\/+$/g; - - // Cached regex for detecting MSIE. - var isExplorer = /msie [\w.]+/; - - // Cached regex for removing a trailing slash. - var trailingSlash = /\/$/; - - // Cached regex for stripping urls of hash. - var pathStripper = /#.*$/; - - // Has the history handling already been started? - History.started = false; - - // Set up all inheritable **Backbone.History** properties and methods. - _.extend(History.prototype, Events, { - - // The default interval to poll for hash changes, if necessary, is - // twenty times a second. - interval: 50, - - // Are we at the app root? - atRoot: function() { - return this.location.pathname.replace(/[^\/]$/, '$&/') === this.root; - }, - - // Gets the true hash value. Cannot use location.hash directly due to bug - // in Firefox where location.hash will always be decoded. - getHash: function(window) { - var match = (window || this).location.href.match(/#(.*)$/); - return match ? match[1] : ''; - }, - - // Get the cross-browser normalized URL fragment, either from the URL, - // the hash, or the override. - getFragment: function(fragment, forcePushState) { - if (fragment == null) { - if (this._hasPushState || !this._wantsHashChange || forcePushState) { - fragment = decodeURI(this.location.pathname + this.location.search); - var root = this.root.replace(trailingSlash, ''); - if (!fragment.indexOf(root)) fragment = fragment.slice(root.length); - } else { - fragment = this.getHash(); - } - } - return fragment.replace(routeStripper, ''); - }, - - // Start the hash change handling, returning `true` if the current URL matches - // an existing route, and `false` otherwise. - start: function(options) { - if (History.started) throw new Error("Backbone.history has already been started"); - History.started = true; - - // Figure out the initial configuration. Do we need an iframe? - // Is pushState desired ... is it available? - this.options = _.extend({root: '/'}, this.options, options); - this.root = this.options.root; - this._wantsHashChange = this.options.hashChange !== false; - this._wantsPushState = !!this.options.pushState; - this._hasPushState = !!(this.options.pushState && this.history && this.history.pushState); - var fragment = this.getFragment(); - var docMode = document.documentMode; - var oldIE = (isExplorer.exec(navigator.userAgent.toLowerCase()) && (!docMode || docMode <= 7)); - - // Normalize root to always include a leading and trailing slash. - this.root = ('/' + this.root + '/').replace(rootStripper, '/'); - - if (oldIE && this._wantsHashChange) { - var frame = Backbone.$('",o=new cdb.ui.common.ShareDialog({title:e.map.get("title"),description:e.map.get("description"),model:t.map,code:s,url:e.url,public_map_url:i,share_url:e.share_url,template:n,target:$(".cartodb-share a"),size:$(document).width()>400?"":"small",width:$(document).width()>400?430:216});return o.render()}),cdb.vis.Overlay.register("search",function(e,t){var n=cdb.core.Template.compile(e.template||'
    ',e.templateType||"mustache"),r=new cdb.geo.ui.Search({template:n,model:t.map});return r.render()}),cdb.vis.Overlay.register("tooltip",function(e,t){var n;if(!e.layer){var r=t.getLayers();r.length>1&&(n=r[1]),e.layer=n}if(!e.layer)throw new Error("layer is null");e.layer.setInteraction(!0);var i=new cdb.geo.ui.Tooltip(e);return i}),cdb.vis.Overlay.register("infobox",function(e,t){var n,r=t.getLayers();e.layer||(r.length>1&&(n=r[1]),e.layer=n);if(!e.layer)throw new Error("layer is null");e.layer.setInteraction(!0);var i=new cdb.geo.ui.InfoBox(e);return i})}(),function(){function n(e){for(var n in t)if(e.indexOf(n)!==-1)return e.replace(n,t[n]);return e}function r(e,t){t.infowindow&&t.infowindow.fields&&(t.interactivity?t.interactivity.indexOf("cartodb_id")===-1&&(t.interactivity=t.interactivity+",cartodb_id"):t.interactivity="cartodb_id"),e.https&&(t.tiler_protocol="https",t.tiler_port=443,t.sql_api_protocol="https",t.sql_api_port=443),t.cartodb_logo=e.cartodb_logo==undefined?t.cartodb_logo:e.cartodb_logo}var e=cdb.vis.Layers,t={"https://dnv9my2eseobd.cloudfront.net/":"http://a.tiles.mapbox.com/","https://maps.nlp.nokia.com/":"http://maps.nlp.nokia.com/","https://tile.stamen.com/":"http://tile.stamen.com/","https://{s}.maps.nlp.nokia.com/":"http://{s}.maps.nlp.nokia.com/","https://cartocdn_{s}.global.ssl.fastly.net/":"http://{s}.api.cartocdn.com/"};e.register("tilejson",function(e,t){var r=t.tiles[0];return r=e.https?r:n(r),new cdb.geo.TileLayer({urlTemplate:r})}),e.register("tiled",function(e,t){var r=t.urlTemplate;return r=e.https?r:n(r),t.urlTemplate=r,new cdb.geo.TileLayer(t)}),e.register("wms",function(e,t){return new cdb.geo.WMSLayer(t)}),e.register("gmapsbase",function(e,t){return new cdb.geo.GMapsBaseLayer(t)}),e.register("plain",function(e,t){return new cdb.geo.PlainLayer(t)}),e.register("background",function(e,t){return new cdb.geo.PlainLayer(t)});var i=function(e,t){return r(e,t),t.sublayers?(t.type="layergroup",new cdb.geo.CartoDBGroupLayer(t)):new cdb.geo.CartoDBLayer(t)};e.register("cartodb",i),e.register("carto",i),e.register("layergroup",function(e,t){return r(e,t),new cdb.geo.CartoDBGroupLayer(t)}),e.register("namedmap",function(e,t){return r(e,t),new cdb.geo.CartoDBNamedMapLayer(t)}),e.register("torque",function(e,t){return e.https&&t.sql_api_domain&&t.sql_api_domain.indexOf("cartodb.com")!==-1&&(t.sql_api_protocol="https",t.sql_api_port=443,t.tiler_protocol="https",t.tiler_port=443),t.cartodb_logo=e.cartodb_logo==undefined?t.cartodb_logo:e.cartodb_logo,new cdb.geo.TorqueLayer(t)})}(),function(){function e(){}function n(e){var t=e.host||"cartodb.com",n=e.protocol||"https";return n+"://"+e.user+"."+t+"/api/v1/viz/"+e.table+"/viz.json"}function r(e,t){var r=null;if(e.layers!==undefined||(e.kind||e.type)!==undefined){_.defer(function(){t(e)});return}e.table!==undefined&&e.user!==undefined?r=n(e):e.indexOf&&e.indexOf("http")===0&&(r=e),r?cdb.vis.Loader.get(r,t):_.defer(function(){t(null)})}_.extend(e.prototype,Backbone.Events,{done:function(e){return this.bind("done",e)},error:function(e){return this.bind("error",e)}}),cdb._Promise=e;var t={};cartodb.createLayer=function(t,n,i,s){var o=new e,u,a;i=i||{};if(t===undefined)throw new TypeError("map should be provided");if(n===undefined)throw new TypeError("layer should be provided");var f=arguments,l=f[f.length-1];return _.isFunction(l)&&(s=l),o.addTo=function(e,t){return o.on("done",function(){a.addLayerToMap(u,e,t)}),o},r(n,function(e){function c(){u=f.createLayer(n,{no_base_layer:!0});if(!u)return o.trigger("error","layer not supported"),o;i.infowindow&&f.addInfowindow(u),i.tooltip&&f.addTooltip(u),i.legends&&f.addLegends([n]),i.time_slider&&u.model.get("type")==="torque"&&f.addTimeSlider(u),s&&s(u),o.trigger("done",u)}var n;if(!e){o.trigger("error");return}if(e.layers){e.layers.length<2&&o.trigger("error","visualization file does not contain layer info");var r=i.layerIndex===undefined?1:i.layerIndex;if(e.layers.length<=r){o.trigger("error","layerIndex out of bounds");return}n=e.layers[r]}else n=e;if(!n){o.trigger("error");return}i&&!_.isFunction(i)&&(n.options=n.options||{},_.extend(n.options,i)),i=_.defaults(i,{infowindow:!0,https:!1,legends:!0,time_slider:!0,tooltip:!0});if(typeof t.overlayMapTypes!="undefined")a=cdb.geo.GoogleMapsMapView;else{if(!(t instanceof L.Map||window.L&&t instanceof window.L.Map))return o.trigger("error","cartodb.js can't guess the map type"),o;a=cdb.geo.LeafletMapView}var f=t.viz;if(!f){var l=new a({map_object:t,map:new cdb.geo.Map});t.viz=f=new cdb.vis.Vis({mapView:l}),f.updated_at=e.updated_at,f.https=i.https}f.checkModules([n])?c():f.loadModules([n],function(){c()})}),o}}(),function(){function t(e){if(cartodb===this||window===this)return new t(e);if(!e.user)throw new Error("user should be provided");var n=new String(window.location.protocol);n=n.slice(0,n.length-1),n=="file"&&(n="https"),this.ajax=e.ajax||(typeof jQuery!="undefined"?jQuery.ajax:reqwest);if(!this.ajax)throw new Error("jQuery or reqwest should be loaded");this.options=_.defaults(e,{version:"v2",protocol:n,jsonp:typeof jQuery!="undefined"?!jQuery.support.cors:!1})}var e=this;e.cartodb=e.cartodb||{},t.prototype._host=function(){var e=this.options;if(e&&e.completeDomain)return e.completeDomain+"/api/"+e.version+"/sql";var t=e.host||"cartodb.com",n=e.protocol||"https";return n+"://"+e.user+"."+t+"/api/"+e.version+"/sql"},t.prototype.execute=function(e,t,n,r){var i=new cartodb._Promise;if(!e)throw new TypeError("sql should not be null");var s=arguments,o=s[s.length-1];_.isFunction(o)&&(r=o),n=_.defaults(n||{},this.options);var u={type:"get",dataType:"json",crossDomain:!0};n.jsonp&&(delete u.crossDomain,u.dataType="jsonp"),n.cache&&(u.cache=n.cache);var a="156543.03515625",f="ST_MakeEnvelope(-20037508.5,-20037508.5,20037508.5,20037508.5,3857)";e=e.replace("!bbox!",f).replace("!pixel_width!",a).replace("!pixel_height!",a);var l=Mustache.render(e,t),c="q="+encodeURIComponent(l),h=["format","dp","api_key"];n.extra_params&&(h=h.concat(n.extra_params));for(var p in h){var d=h[p],v=n[d];v&&(c+="&"+d+"="+v)}var m=n.type?n.type=="get":u.type=="get";u.url=this._host(),m?u.url+="?"+c:u.data=c;var g=n.success,y=n.error;return g&&delete n.success,y&&delete y.success,u.error=function(e){var t=e.responseText||e.response,n=t&&JSON.parse(t);i.trigger("error",n&&n.error,e),y&&y(e)},u.success=function(e,t,n){t==undefined&&(t=e.status,n=e,e=JSON.parse(e.response)),i.trigger("done",e,t,n),g&&g(e,t,n),r&&r(e)},delete n.jsonp,this.ajax(_.extend(u,n)),i},t.prototype.getBounds=function(e,t,n,r){var i=new cartodb._Promise,s=arguments,o=s[s.length-1];_.isFunction(o)&&(r=o);var u="SELECT ST_XMin(ST_Extent(the_geom)) as minx, ST_YMin(ST_Extent(the_geom)) as miny, ST_XMax(ST_Extent(the_geom)) as maxx, ST_YMax(ST_Extent(the_geom)) as maxy from ({{{ sql }}}) as subq";return e=Mustache.render(e,t),this.execute(u,{sql:e},n).done(function(e){if(e.rows&&e.rows.length>0&&e.rows[0].maxx!=null){var t=e.rows[0],n=-85.0511,s=85.0511,o=-179,u=179,a=function(e,t,n){return en?n:e},f=a(t.maxx,o,u),l=a(t.minx,o,u),c=a(t.maxy,n,s),h=a(t.miny,n,s),p=[[c,f],[h,l]];i.trigger("done",p),r&&r(p)}}).error(function(e){i.trigger("error",e)}),i},t.prototype.table=function(e){function a(){a.fetch.apply(a,arguments)}var t=e,n,r=[],i,s,o,u=this;return a.fetch=function(e){e=e||{};var t=arguments,n=t[t.length-1];_.isFunction(n)&&(callback=n,t.length===1&&(e={})),u.execute(a.sql(),e,callback)},a.sql=function(){var e="select";return r.length?e+=" "+r.join(",")+" ":e+=" * ",e+="from "+t,n&&(e+=" where "+n),i&&(e+=" limit "+i),s&&(e+=" order by "+s),o&&(e+=" "+o),e},a.filter=function(e){return n=e,a},a.order_by=function(e){return s=e,a},a.asc=function(){return o="asc",a},a.desc=function(){return o="desc",a},a.columns=function(e){return r=e,a},a.limit=function(e){return i=e,a},a},e.cartodb.SQL=t}(),function(){cartodb.createVis=function(e,t,n,r){if(!e)throw new TypeError("a DOM element should be provided");var i=arguments,s=i[i.length-1];_.isFunction(s)&&(r=s),e=typeof e=="string"?document.getElementById(e):e;var o=new cartodb.vis.Vis({el:e});return t&&(o.load(t,n),r&&o.done(r)),o}}(),cdb.$=$,cdb.L=L,cdb.Mustache=Mustache,cdb.Backbone=Backbone,cdb._=_}();for(var i in __prev)__prev[i]&&(window[i]=__prev[i])})(); \ No newline at end of file diff --git a/vendor/assets/javascripts/d3.js b/vendor/assets/javascripts/d3.js deleted file mode 100644 index a972735650..0000000000 --- a/vendor/assets/javascripts/d3.js +++ /dev/null @@ -1,9255 +0,0 @@ -!function() { - var d3 = { - version: "3.4.8" - }; - if (!Date.now) Date.now = function() { - return +new Date(); - }; - var d3_arraySlice = [].slice, d3_array = function(list) { - return d3_arraySlice.call(list); - }; - var d3_document = document, d3_documentElement = d3_document.documentElement, d3_window = window; - try { - d3_array(d3_documentElement.childNodes)[0].nodeType; - } catch (e) { - d3_array = function(list) { - var i = list.length, array = new Array(i); - while (i--) array[i] = list[i]; - return array; - }; - } - try { - d3_document.createElement("div").style.setProperty("opacity", 0, ""); - } catch (error) { - var d3_element_prototype = d3_window.Element.prototype, d3_element_setAttribute = d3_element_prototype.setAttribute, d3_element_setAttributeNS = d3_element_prototype.setAttributeNS, d3_style_prototype = d3_window.CSSStyleDeclaration.prototype, d3_style_setProperty = d3_style_prototype.setProperty; - d3_element_prototype.setAttribute = function(name, value) { - d3_element_setAttribute.call(this, name, value + ""); - }; - d3_element_prototype.setAttributeNS = function(space, local, value) { - d3_element_setAttributeNS.call(this, space, local, value + ""); - }; - d3_style_prototype.setProperty = function(name, value, priority) { - d3_style_setProperty.call(this, name, value + "", priority); - }; - } - d3.ascending = d3_ascending; - function d3_ascending(a, b) { - return a < b ? -1 : a > b ? 1 : a >= b ? 0 : NaN; - } - d3.descending = function(a, b) { - return b < a ? -1 : b > a ? 1 : b >= a ? 0 : NaN; - }; - d3.min = function(array, f) { - var i = -1, n = array.length, a, b; - if (arguments.length === 1) { - while (++i < n && !((a = array[i]) != null && a <= a)) a = undefined; - while (++i < n) if ((b = array[i]) != null && a > b) a = b; - } else { - while (++i < n && !((a = f.call(array, array[i], i)) != null && a <= a)) a = undefined; - while (++i < n) if ((b = f.call(array, array[i], i)) != null && a > b) a = b; - } - return a; - }; - d3.max = function(array, f) { - var i = -1, n = array.length, a, b; - if (arguments.length === 1) { - while (++i < n && !((a = array[i]) != null && a <= a)) a = undefined; - while (++i < n) if ((b = array[i]) != null && b > a) a = b; - } else { - while (++i < n && !((a = f.call(array, array[i], i)) != null && a <= a)) a = undefined; - while (++i < n) if ((b = f.call(array, array[i], i)) != null && b > a) a = b; - } - return a; - }; - d3.extent = function(array, f) { - var i = -1, n = array.length, a, b, c; - if (arguments.length === 1) { - while (++i < n && !((a = c = array[i]) != null && a <= a)) a = c = undefined; - while (++i < n) if ((b = array[i]) != null) { - if (a > b) a = b; - if (c < b) c = b; - } - } else { - while (++i < n && !((a = c = f.call(array, array[i], i)) != null && a <= a)) a = undefined; - while (++i < n) if ((b = f.call(array, array[i], i)) != null) { - if (a > b) a = b; - if (c < b) c = b; - } - } - return [ a, c ]; - }; - d3.sum = function(array, f) { - var s = 0, n = array.length, a, i = -1; - if (arguments.length === 1) { - while (++i < n) if (!isNaN(a = +array[i])) s += a; - } else { - while (++i < n) if (!isNaN(a = +f.call(array, array[i], i))) s += a; - } - return s; - }; - function d3_number(x) { - return x != null && !isNaN(x); - } - d3.mean = function(array, f) { - var s = 0, n = array.length, a, i = -1, j = n; - if (arguments.length === 1) { - while (++i < n) if (d3_number(a = array[i])) s += a; else --j; - } else { - while (++i < n) if (d3_number(a = f.call(array, array[i], i))) s += a; else --j; - } - return j ? s / j : undefined; - }; - d3.quantile = function(values, p) { - var H = (values.length - 1) * p + 1, h = Math.floor(H), v = +values[h - 1], e = H - h; - return e ? v + e * (values[h] - v) : v; - }; - d3.median = function(array, f) { - if (arguments.length > 1) array = array.map(f); - array = array.filter(d3_number); - return array.length ? d3.quantile(array.sort(d3_ascending), .5) : undefined; - }; - function d3_bisector(compare) { - return { - left: function(a, x, lo, hi) { - if (arguments.length < 3) lo = 0; - if (arguments.length < 4) hi = a.length; - while (lo < hi) { - var mid = lo + hi >>> 1; - if (compare(a[mid], x) < 0) lo = mid + 1; else hi = mid; - } - return lo; - }, - right: function(a, x, lo, hi) { - if (arguments.length < 3) lo = 0; - if (arguments.length < 4) hi = a.length; - while (lo < hi) { - var mid = lo + hi >>> 1; - if (compare(a[mid], x) > 0) hi = mid; else lo = mid + 1; - } - return lo; - } - }; - } - var d3_bisect = d3_bisector(d3_ascending); - d3.bisectLeft = d3_bisect.left; - d3.bisect = d3.bisectRight = d3_bisect.right; - d3.bisector = function(f) { - return d3_bisector(f.length === 1 ? function(d, x) { - return d3_ascending(f(d), x); - } : f); - }; - d3.shuffle = function(array) { - var m = array.length, t, i; - while (m) { - i = Math.random() * m-- | 0; - t = array[m], array[m] = array[i], array[i] = t; - } - return array; - }; - d3.permute = function(array, indexes) { - var i = indexes.length, permutes = new Array(i); - while (i--) permutes[i] = array[indexes[i]]; - return permutes; - }; - d3.pairs = function(array) { - var i = 0, n = array.length - 1, p0, p1 = array[0], pairs = new Array(n < 0 ? 0 : n); - while (i < n) pairs[i] = [ p0 = p1, p1 = array[++i] ]; - return pairs; - }; - d3.zip = function() { - if (!(n = arguments.length)) return []; - for (var i = -1, m = d3.min(arguments, d3_zipLength), zips = new Array(m); ++i < m; ) { - for (var j = -1, n, zip = zips[i] = new Array(n); ++j < n; ) { - zip[j] = arguments[j][i]; - } - } - return zips; - }; - function d3_zipLength(d) { - return d.length; - } - d3.transpose = function(matrix) { - return d3.zip.apply(d3, matrix); - }; - d3.keys = function(map) { - var keys = []; - for (var key in map) keys.push(key); - return keys; - }; - d3.values = function(map) { - var values = []; - for (var key in map) values.push(map[key]); - return values; - }; - d3.entries = function(map) { - var entries = []; - for (var key in map) entries.push({ - key: key, - value: map[key] - }); - return entries; - }; - d3.merge = function(arrays) { - var n = arrays.length, m, i = -1, j = 0, merged, array; - while (++i < n) j += arrays[i].length; - merged = new Array(j); - while (--n >= 0) { - array = arrays[n]; - m = array.length; - while (--m >= 0) { - merged[--j] = array[m]; - } - } - return merged; - }; - var abs = Math.abs; - d3.range = function(start, stop, step) { - if (arguments.length < 3) { - step = 1; - if (arguments.length < 2) { - stop = start; - start = 0; - } - } - if ((stop - start) / step === Infinity) throw new Error("infinite range"); - var range = [], k = d3_range_integerScale(abs(step)), i = -1, j; - start *= k, stop *= k, step *= k; - if (step < 0) while ((j = start + step * ++i) > stop) range.push(j / k); else while ((j = start + step * ++i) < stop) range.push(j / k); - return range; - }; - function d3_range_integerScale(x) { - var k = 1; - while (x * k % 1) k *= 10; - return k; - } - function d3_class(ctor, properties) { - try { - for (var key in properties) { - Object.defineProperty(ctor.prototype, key, { - value: properties[key], - enumerable: false - }); - } - } catch (e) { - ctor.prototype = properties; - } - } - d3.map = function(object) { - var map = new d3_Map(); - if (object instanceof d3_Map) object.forEach(function(key, value) { - map.set(key, value); - }); else for (var key in object) map.set(key, object[key]); - return map; - }; - function d3_Map() {} - d3_class(d3_Map, { - has: d3_map_has, - get: function(key) { - return this[d3_map_prefix + key]; - }, - set: function(key, value) { - return this[d3_map_prefix + key] = value; - }, - remove: d3_map_remove, - keys: d3_map_keys, - values: function() { - var values = []; - this.forEach(function(key, value) { - values.push(value); - }); - return values; - }, - entries: function() { - var entries = []; - this.forEach(function(key, value) { - entries.push({ - key: key, - value: value - }); - }); - return entries; - }, - size: d3_map_size, - empty: d3_map_empty, - forEach: function(f) { - for (var key in this) if (key.charCodeAt(0) === d3_map_prefixCode) f.call(this, key.substring(1), this[key]); - } - }); - var d3_map_prefix = "\x00", d3_map_prefixCode = d3_map_prefix.charCodeAt(0); - function d3_map_has(key) { - return d3_map_prefix + key in this; - } - function d3_map_remove(key) { - key = d3_map_prefix + key; - return key in this && delete this[key]; - } - function d3_map_keys() { - var keys = []; - this.forEach(function(key) { - keys.push(key); - }); - return keys; - } - function d3_map_size() { - var size = 0; - for (var key in this) if (key.charCodeAt(0) === d3_map_prefixCode) ++size; - return size; - } - function d3_map_empty() { - for (var key in this) if (key.charCodeAt(0) === d3_map_prefixCode) return false; - return true; - } - d3.nest = function() { - var nest = {}, keys = [], sortKeys = [], sortValues, rollup; - function map(mapType, array, depth) { - if (depth >= keys.length) return rollup ? rollup.call(nest, array) : sortValues ? array.sort(sortValues) : array; - var i = -1, n = array.length, key = keys[depth++], keyValue, object, setter, valuesByKey = new d3_Map(), values; - while (++i < n) { - if (values = valuesByKey.get(keyValue = key(object = array[i]))) { - values.push(object); - } else { - valuesByKey.set(keyValue, [ object ]); - } - } - if (mapType) { - object = mapType(); - setter = function(keyValue, values) { - object.set(keyValue, map(mapType, values, depth)); - }; - } else { - object = {}; - setter = function(keyValue, values) { - object[keyValue] = map(mapType, values, depth); - }; - } - valuesByKey.forEach(setter); - return object; - } - function entries(map, depth) { - if (depth >= keys.length) return map; - var array = [], sortKey = sortKeys[depth++]; - map.forEach(function(key, keyMap) { - array.push({ - key: key, - values: entries(keyMap, depth) - }); - }); - return sortKey ? array.sort(function(a, b) { - return sortKey(a.key, b.key); - }) : array; - } - nest.map = function(array, mapType) { - return map(mapType, array, 0); - }; - nest.entries = function(array) { - return entries(map(d3.map, array, 0), 0); - }; - nest.key = function(d) { - keys.push(d); - return nest; - }; - nest.sortKeys = function(order) { - sortKeys[keys.length - 1] = order; - return nest; - }; - nest.sortValues = function(order) { - sortValues = order; - return nest; - }; - nest.rollup = function(f) { - rollup = f; - return nest; - }; - return nest; - }; - d3.set = function(array) { - var set = new d3_Set(); - if (array) for (var i = 0, n = array.length; i < n; ++i) set.add(array[i]); - return set; - }; - function d3_Set() {} - d3_class(d3_Set, { - has: d3_map_has, - add: function(value) { - this[d3_map_prefix + value] = true; - return value; - }, - remove: function(value) { - value = d3_map_prefix + value; - return value in this && delete this[value]; - }, - values: d3_map_keys, - size: d3_map_size, - empty: d3_map_empty, - forEach: function(f) { - for (var value in this) if (value.charCodeAt(0) === d3_map_prefixCode) f.call(this, value.substring(1)); - } - }); - d3.behavior = {}; - d3.rebind = function(target, source) { - var i = 1, n = arguments.length, method; - while (++i < n) target[method = arguments[i]] = d3_rebind(target, source, source[method]); - return target; - }; - function d3_rebind(target, source, method) { - return function() { - var value = method.apply(source, arguments); - return value === source ? target : value; - }; - } - function d3_vendorSymbol(object, name) { - if (name in object) return name; - name = name.charAt(0).toUpperCase() + name.substring(1); - for (var i = 0, n = d3_vendorPrefixes.length; i < n; ++i) { - var prefixName = d3_vendorPrefixes[i] + name; - if (prefixName in object) return prefixName; - } - } - var d3_vendorPrefixes = [ "webkit", "ms", "moz", "Moz", "o", "O" ]; - function d3_noop() {} - d3.dispatch = function() { - var dispatch = new d3_dispatch(), i = -1, n = arguments.length; - while (++i < n) dispatch[arguments[i]] = d3_dispatch_event(dispatch); - return dispatch; - }; - function d3_dispatch() {} - d3_dispatch.prototype.on = function(type, listener) { - var i = type.indexOf("."), name = ""; - if (i >= 0) { - name = type.substring(i + 1); - type = type.substring(0, i); - } - if (type) return arguments.length < 2 ? this[type].on(name) : this[type].on(name, listener); - if (arguments.length === 2) { - if (listener == null) for (type in this) { - if (this.hasOwnProperty(type)) this[type].on(name, null); - } - return this; - } - }; - function d3_dispatch_event(dispatch) { - var listeners = [], listenerByName = new d3_Map(); - function event() { - var z = listeners, i = -1, n = z.length, l; - while (++i < n) if (l = z[i].on) l.apply(this, arguments); - return dispatch; - } - event.on = function(name, listener) { - var l = listenerByName.get(name), i; - if (arguments.length < 2) return l && l.on; - if (l) { - l.on = null; - listeners = listeners.slice(0, i = listeners.indexOf(l)).concat(listeners.slice(i + 1)); - listenerByName.remove(name); - } - if (listener) listeners.push(listenerByName.set(name, { - on: listener - })); - return dispatch; - }; - return event; - } - d3.event = null; - function d3_eventPreventDefault() { - d3.event.preventDefault(); - } - function d3_eventSource() { - var e = d3.event, s; - while (s = e.sourceEvent) e = s; - return e; - } - function d3_eventDispatch(target) { - var dispatch = new d3_dispatch(), i = 0, n = arguments.length; - while (++i < n) dispatch[arguments[i]] = d3_dispatch_event(dispatch); - dispatch.of = function(thiz, argumentz) { - return function(e1) { - try { - var e0 = e1.sourceEvent = d3.event; - e1.target = target; - d3.event = e1; - dispatch[e1.type].apply(thiz, argumentz); - } finally { - d3.event = e0; - } - }; - }; - return dispatch; - } - d3.requote = function(s) { - return s.replace(d3_requote_re, "\\$&"); - }; - var d3_requote_re = /[\\\^\$\*\+\?\|\[\]\(\)\.\{\}]/g; - var d3_subclass = {}.__proto__ ? function(object, prototype) { - object.__proto__ = prototype; - } : function(object, prototype) { - for (var property in prototype) object[property] = prototype[property]; - }; - function d3_selection(groups) { - d3_subclass(groups, d3_selectionPrototype); - return groups; - } - var d3_select = function(s, n) { - return n.querySelector(s); - }, d3_selectAll = function(s, n) { - return n.querySelectorAll(s); - }, d3_selectMatcher = d3_documentElement[d3_vendorSymbol(d3_documentElement, "matchesSelector")], d3_selectMatches = function(n, s) { - return d3_selectMatcher.call(n, s); - }; - if (typeof Sizzle === "function") { - d3_select = function(s, n) { - return Sizzle(s, n)[0] || null; - }; - d3_selectAll = Sizzle; - d3_selectMatches = Sizzle.matchesSelector; - } - d3.selection = function() { - return d3_selectionRoot; - }; - var d3_selectionPrototype = d3.selection.prototype = []; - d3_selectionPrototype.select = function(selector) { - var subgroups = [], subgroup, subnode, group, node; - selector = d3_selection_selector(selector); - for (var j = -1, m = this.length; ++j < m; ) { - subgroups.push(subgroup = []); - subgroup.parentNode = (group = this[j]).parentNode; - for (var i = -1, n = group.length; ++i < n; ) { - if (node = group[i]) { - subgroup.push(subnode = selector.call(node, node.__data__, i, j)); - if (subnode && "__data__" in node) subnode.__data__ = node.__data__; - } else { - subgroup.push(null); - } - } - } - return d3_selection(subgroups); - }; - function d3_selection_selector(selector) { - return typeof selector === "function" ? selector : function() { - return d3_select(selector, this); - }; - } - d3_selectionPrototype.selectAll = function(selector) { - var subgroups = [], subgroup, node; - selector = d3_selection_selectorAll(selector); - for (var j = -1, m = this.length; ++j < m; ) { - for (var group = this[j], i = -1, n = group.length; ++i < n; ) { - if (node = group[i]) { - subgroups.push(subgroup = d3_array(selector.call(node, node.__data__, i, j))); - subgroup.parentNode = node; - } - } - } - return d3_selection(subgroups); - }; - function d3_selection_selectorAll(selector) { - return typeof selector === "function" ? selector : function() { - return d3_selectAll(selector, this); - }; - } - var d3_nsPrefix = { - svg: "http://www.w3.org/2000/svg", - xhtml: "http://www.w3.org/1999/xhtml", - xlink: "http://www.w3.org/1999/xlink", - xml: "http://www.w3.org/XML/1998/namespace", - xmlns: "http://www.w3.org/2000/xmlns/" - }; - d3.ns = { - prefix: d3_nsPrefix, - qualify: function(name) { - var i = name.indexOf(":"), prefix = name; - if (i >= 0) { - prefix = name.substring(0, i); - name = name.substring(i + 1); - } - return d3_nsPrefix.hasOwnProperty(prefix) ? { - space: d3_nsPrefix[prefix], - local: name - } : name; - } - }; - d3_selectionPrototype.attr = function(name, value) { - if (arguments.length < 2) { - if (typeof name === "string") { - var node = this.node(); - name = d3.ns.qualify(name); - return name.local ? node.getAttributeNS(name.space, name.local) : node.getAttribute(name); - } - for (value in name) this.each(d3_selection_attr(value, name[value])); - return this; - } - return this.each(d3_selection_attr(name, value)); - }; - function d3_selection_attr(name, value) { - name = d3.ns.qualify(name); - function attrNull() { - this.removeAttribute(name); - } - function attrNullNS() { - this.removeAttributeNS(name.space, name.local); - } - function attrConstant() { - this.setAttribute(name, value); - } - function attrConstantNS() { - this.setAttributeNS(name.space, name.local, value); - } - function attrFunction() { - var x = value.apply(this, arguments); - if (x == null) this.removeAttribute(name); else this.setAttribute(name, x); - } - function attrFunctionNS() { - var x = value.apply(this, arguments); - if (x == null) this.removeAttributeNS(name.space, name.local); else this.setAttributeNS(name.space, name.local, x); - } - return value == null ? name.local ? attrNullNS : attrNull : typeof value === "function" ? name.local ? attrFunctionNS : attrFunction : name.local ? attrConstantNS : attrConstant; - } - function d3_collapse(s) { - return s.trim().replace(/\s+/g, " "); - } - d3_selectionPrototype.classed = function(name, value) { - if (arguments.length < 2) { - if (typeof name === "string") { - var node = this.node(), n = (name = d3_selection_classes(name)).length, i = -1; - if (value = node.classList) { - while (++i < n) if (!value.contains(name[i])) return false; - } else { - value = node.getAttribute("class"); - while (++i < n) if (!d3_selection_classedRe(name[i]).test(value)) return false; - } - return true; - } - for (value in name) this.each(d3_selection_classed(value, name[value])); - return this; - } - return this.each(d3_selection_classed(name, value)); - }; - function d3_selection_classedRe(name) { - return new RegExp("(?:^|\\s+)" + d3.requote(name) + "(?:\\s+|$)", "g"); - } - function d3_selection_classes(name) { - return name.trim().split(/^|\s+/); - } - function d3_selection_classed(name, value) { - name = d3_selection_classes(name).map(d3_selection_classedName); - var n = name.length; - function classedConstant() { - var i = -1; - while (++i < n) name[i](this, value); - } - function classedFunction() { - var i = -1, x = value.apply(this, arguments); - while (++i < n) name[i](this, x); - } - return typeof value === "function" ? classedFunction : classedConstant; - } - function d3_selection_classedName(name) { - var re = d3_selection_classedRe(name); - return function(node, value) { - if (c = node.classList) return value ? c.add(name) : c.remove(name); - var c = node.getAttribute("class") || ""; - if (value) { - re.lastIndex = 0; - if (!re.test(c)) node.setAttribute("class", d3_collapse(c + " " + name)); - } else { - node.setAttribute("class", d3_collapse(c.replace(re, " "))); - } - }; - } - d3_selectionPrototype.style = function(name, value, priority) { - var n = arguments.length; - if (n < 3) { - if (typeof name !== "string") { - if (n < 2) value = ""; - for (priority in name) this.each(d3_selection_style(priority, name[priority], value)); - return this; - } - if (n < 2) return d3_window.getComputedStyle(this.node(), null).getPropertyValue(name); - priority = ""; - } - return this.each(d3_selection_style(name, value, priority)); - }; - function d3_selection_style(name, value, priority) { - function styleNull() { - this.style.removeProperty(name); - } - function styleConstant() { - this.style.setProperty(name, value, priority); - } - function styleFunction() { - var x = value.apply(this, arguments); - if (x == null) this.style.removeProperty(name); else this.style.setProperty(name, x, priority); - } - return value == null ? styleNull : typeof value === "function" ? styleFunction : styleConstant; - } - d3_selectionPrototype.property = function(name, value) { - if (arguments.length < 2) { - if (typeof name === "string") return this.node()[name]; - for (value in name) this.each(d3_selection_property(value, name[value])); - return this; - } - return this.each(d3_selection_property(name, value)); - }; - function d3_selection_property(name, value) { - function propertyNull() { - delete this[name]; - } - function propertyConstant() { - this[name] = value; - } - function propertyFunction() { - var x = value.apply(this, arguments); - if (x == null) delete this[name]; else this[name] = x; - } - return value == null ? propertyNull : typeof value === "function" ? propertyFunction : propertyConstant; - } - d3_selectionPrototype.text = function(value) { - return arguments.length ? this.each(typeof value === "function" ? function() { - var v = value.apply(this, arguments); - this.textContent = v == null ? "" : v; - } : value == null ? function() { - this.textContent = ""; - } : function() { - this.textContent = value; - }) : this.node().textContent; - }; - d3_selectionPrototype.html = function(value) { - return arguments.length ? this.each(typeof value === "function" ? function() { - var v = value.apply(this, arguments); - this.innerHTML = v == null ? "" : v; - } : value == null ? function() { - this.innerHTML = ""; - } : function() { - this.innerHTML = value; - }) : this.node().innerHTML; - }; - d3_selectionPrototype.append = function(name) { - name = d3_selection_creator(name); - return this.select(function() { - return this.appendChild(name.apply(this, arguments)); - }); - }; - function d3_selection_creator(name) { - return typeof name === "function" ? name : (name = d3.ns.qualify(name)).local ? function() { - return this.ownerDocument.createElementNS(name.space, name.local); - } : function() { - return this.ownerDocument.createElementNS(this.namespaceURI, name); - }; - } - d3_selectionPrototype.insert = function(name, before) { - name = d3_selection_creator(name); - before = d3_selection_selector(before); - return this.select(function() { - return this.insertBefore(name.apply(this, arguments), before.apply(this, arguments) || null); - }); - }; - d3_selectionPrototype.remove = function() { - return this.each(function() { - var parent = this.parentNode; - if (parent) parent.removeChild(this); - }); - }; - d3_selectionPrototype.data = function(value, key) { - var i = -1, n = this.length, group, node; - if (!arguments.length) { - value = new Array(n = (group = this[0]).length); - while (++i < n) { - if (node = group[i]) { - value[i] = node.__data__; - } - } - return value; - } - function bind(group, groupData) { - var i, n = group.length, m = groupData.length, n0 = Math.min(n, m), updateNodes = new Array(m), enterNodes = new Array(m), exitNodes = new Array(n), node, nodeData; - if (key) { - var nodeByKeyValue = new d3_Map(), dataByKeyValue = new d3_Map(), keyValues = [], keyValue; - for (i = -1; ++i < n; ) { - keyValue = key.call(node = group[i], node.__data__, i); - if (nodeByKeyValue.has(keyValue)) { - exitNodes[i] = node; - } else { - nodeByKeyValue.set(keyValue, node); - } - keyValues.push(keyValue); - } - for (i = -1; ++i < m; ) { - keyValue = key.call(groupData, nodeData = groupData[i], i); - if (node = nodeByKeyValue.get(keyValue)) { - updateNodes[i] = node; - node.__data__ = nodeData; - } else if (!dataByKeyValue.has(keyValue)) { - enterNodes[i] = d3_selection_dataNode(nodeData); - } - dataByKeyValue.set(keyValue, nodeData); - nodeByKeyValue.remove(keyValue); - } - for (i = -1; ++i < n; ) { - if (nodeByKeyValue.has(keyValues[i])) { - exitNodes[i] = group[i]; - } - } - } else { - for (i = -1; ++i < n0; ) { - node = group[i]; - nodeData = groupData[i]; - if (node) { - node.__data__ = nodeData; - updateNodes[i] = node; - } else { - enterNodes[i] = d3_selection_dataNode(nodeData); - } - } - for (;i < m; ++i) { - enterNodes[i] = d3_selection_dataNode(groupData[i]); - } - for (;i < n; ++i) { - exitNodes[i] = group[i]; - } - } - enterNodes.update = updateNodes; - enterNodes.parentNode = updateNodes.parentNode = exitNodes.parentNode = group.parentNode; - enter.push(enterNodes); - update.push(updateNodes); - exit.push(exitNodes); - } - var enter = d3_selection_enter([]), update = d3_selection([]), exit = d3_selection([]); - if (typeof value === "function") { - while (++i < n) { - bind(group = this[i], value.call(group, group.parentNode.__data__, i)); - } - } else { - while (++i < n) { - bind(group = this[i], value); - } - } - update.enter = function() { - return enter; - }; - update.exit = function() { - return exit; - }; - return update; - }; - function d3_selection_dataNode(data) { - return { - __data__: data - }; - } - d3_selectionPrototype.datum = function(value) { - return arguments.length ? this.property("__data__", value) : this.property("__data__"); - }; - d3_selectionPrototype.filter = function(filter) { - var subgroups = [], subgroup, group, node; - if (typeof filter !== "function") filter = d3_selection_filter(filter); - for (var j = 0, m = this.length; j < m; j++) { - subgroups.push(subgroup = []); - subgroup.parentNode = (group = this[j]).parentNode; - for (var i = 0, n = group.length; i < n; i++) { - if ((node = group[i]) && filter.call(node, node.__data__, i, j)) { - subgroup.push(node); - } - } - } - return d3_selection(subgroups); - }; - function d3_selection_filter(selector) { - return function() { - return d3_selectMatches(this, selector); - }; - } - d3_selectionPrototype.order = function() { - for (var j = -1, m = this.length; ++j < m; ) { - for (var group = this[j], i = group.length - 1, next = group[i], node; --i >= 0; ) { - if (node = group[i]) { - if (next && next !== node.nextSibling) next.parentNode.insertBefore(node, next); - next = node; - } - } - } - return this; - }; - d3_selectionPrototype.sort = function(comparator) { - comparator = d3_selection_sortComparator.apply(this, arguments); - for (var j = -1, m = this.length; ++j < m; ) this[j].sort(comparator); - return this.order(); - }; - function d3_selection_sortComparator(comparator) { - if (!arguments.length) comparator = d3_ascending; - return function(a, b) { - return a && b ? comparator(a.__data__, b.__data__) : !a - !b; - }; - } - d3_selectionPrototype.each = function(callback) { - return d3_selection_each(this, function(node, i, j) { - callback.call(node, node.__data__, i, j); - }); - }; - function d3_selection_each(groups, callback) { - for (var j = 0, m = groups.length; j < m; j++) { - for (var group = groups[j], i = 0, n = group.length, node; i < n; i++) { - if (node = group[i]) callback(node, i, j); - } - } - return groups; - } - d3_selectionPrototype.call = function(callback) { - var args = d3_array(arguments); - callback.apply(args[0] = this, args); - return this; - }; - d3_selectionPrototype.empty = function() { - return !this.node(); - }; - d3_selectionPrototype.node = function() { - for (var j = 0, m = this.length; j < m; j++) { - for (var group = this[j], i = 0, n = group.length; i < n; i++) { - var node = group[i]; - if (node) return node; - } - } - return null; - }; - d3_selectionPrototype.size = function() { - var n = 0; - this.each(function() { - ++n; - }); - return n; - }; - function d3_selection_enter(selection) { - d3_subclass(selection, d3_selection_enterPrototype); - return selection; - } - var d3_selection_enterPrototype = []; - d3.selection.enter = d3_selection_enter; - d3.selection.enter.prototype = d3_selection_enterPrototype; - d3_selection_enterPrototype.append = d3_selectionPrototype.append; - d3_selection_enterPrototype.empty = d3_selectionPrototype.empty; - d3_selection_enterPrototype.node = d3_selectionPrototype.node; - d3_selection_enterPrototype.call = d3_selectionPrototype.call; - d3_selection_enterPrototype.size = d3_selectionPrototype.size; - d3_selection_enterPrototype.select = function(selector) { - var subgroups = [], subgroup, subnode, upgroup, group, node; - for (var j = -1, m = this.length; ++j < m; ) { - upgroup = (group = this[j]).update; - subgroups.push(subgroup = []); - subgroup.parentNode = group.parentNode; - for (var i = -1, n = group.length; ++i < n; ) { - if (node = group[i]) { - subgroup.push(upgroup[i] = subnode = selector.call(group.parentNode, node.__data__, i, j)); - subnode.__data__ = node.__data__; - } else { - subgroup.push(null); - } - } - } - return d3_selection(subgroups); - }; - d3_selection_enterPrototype.insert = function(name, before) { - if (arguments.length < 2) before = d3_selection_enterInsertBefore(this); - return d3_selectionPrototype.insert.call(this, name, before); - }; - function d3_selection_enterInsertBefore(enter) { - var i0, j0; - return function(d, i, j) { - var group = enter[j].update, n = group.length, node; - if (j != j0) j0 = j, i0 = 0; - if (i >= i0) i0 = i + 1; - while (!(node = group[i0]) && ++i0 < n) ; - return node; - }; - } - d3_selectionPrototype.transition = function() { - var id = d3_transitionInheritId || ++d3_transitionId, subgroups = [], subgroup, node, transition = d3_transitionInherit || { - time: Date.now(), - ease: d3_ease_cubicInOut, - delay: 0, - duration: 250 - }; - for (var j = -1, m = this.length; ++j < m; ) { - subgroups.push(subgroup = []); - for (var group = this[j], i = -1, n = group.length; ++i < n; ) { - if (node = group[i]) d3_transitionNode(node, i, id, transition); - subgroup.push(node); - } - } - return d3_transition(subgroups, id); - }; - d3_selectionPrototype.interrupt = function() { - return this.each(d3_selection_interrupt); - }; - function d3_selection_interrupt() { - var lock = this.__transition__; - if (lock) ++lock.active; - } - d3.select = function(node) { - var group = [ typeof node === "string" ? d3_select(node, d3_document) : node ]; - group.parentNode = d3_documentElement; - return d3_selection([ group ]); - }; - d3.selectAll = function(nodes) { - var group = d3_array(typeof nodes === "string" ? d3_selectAll(nodes, d3_document) : nodes); - group.parentNode = d3_documentElement; - return d3_selection([ group ]); - }; - var d3_selectionRoot = d3.select(d3_documentElement); - d3_selectionPrototype.on = function(type, listener, capture) { - var n = arguments.length; - if (n < 3) { - if (typeof type !== "string") { - if (n < 2) listener = false; - for (capture in type) this.each(d3_selection_on(capture, type[capture], listener)); - return this; - } - if (n < 2) return (n = this.node()["__on" + type]) && n._; - capture = false; - } - return this.each(d3_selection_on(type, listener, capture)); - }; - function d3_selection_on(type, listener, capture) { - var name = "__on" + type, i = type.indexOf("."), wrap = d3_selection_onListener; - if (i > 0) type = type.substring(0, i); - var filter = d3_selection_onFilters.get(type); - if (filter) type = filter, wrap = d3_selection_onFilter; - function onRemove() { - var l = this[name]; - if (l) { - this.removeEventListener(type, l, l.$); - delete this[name]; - } - } - function onAdd() { - var l = wrap(listener, d3_array(arguments)); - onRemove.call(this); - this.addEventListener(type, this[name] = l, l.$ = capture); - l._ = listener; - } - function removeAll() { - var re = new RegExp("^__on([^.]+)" + d3.requote(type) + "$"), match; - for (var name in this) { - if (match = name.match(re)) { - var l = this[name]; - this.removeEventListener(match[1], l, l.$); - delete this[name]; - } - } - } - return i ? listener ? onAdd : onRemove : listener ? d3_noop : removeAll; - } - var d3_selection_onFilters = d3.map({ - mouseenter: "mouseover", - mouseleave: "mouseout" - }); - d3_selection_onFilters.forEach(function(k) { - if ("on" + k in d3_document) d3_selection_onFilters.remove(k); - }); - function d3_selection_onListener(listener, argumentz) { - return function(e) { - var o = d3.event; - d3.event = e; - argumentz[0] = this.__data__; - try { - listener.apply(this, argumentz); - } finally { - d3.event = o; - } - }; - } - function d3_selection_onFilter(listener, argumentz) { - var l = d3_selection_onListener(listener, argumentz); - return function(e) { - var target = this, related = e.relatedTarget; - if (!related || related !== target && !(related.compareDocumentPosition(target) & 8)) { - l.call(target, e); - } - }; - } - var d3_event_dragSelect = "onselectstart" in d3_document ? null : d3_vendorSymbol(d3_documentElement.style, "userSelect"), d3_event_dragId = 0; - function d3_event_dragSuppress() { - var name = ".dragsuppress-" + ++d3_event_dragId, click = "click" + name, w = d3.select(d3_window).on("touchmove" + name, d3_eventPreventDefault).on("dragstart" + name, d3_eventPreventDefault).on("selectstart" + name, d3_eventPreventDefault); - if (d3_event_dragSelect) { - var style = d3_documentElement.style, select = style[d3_event_dragSelect]; - style[d3_event_dragSelect] = "none"; - } - return function(suppressClick) { - w.on(name, null); - if (d3_event_dragSelect) style[d3_event_dragSelect] = select; - if (suppressClick) { - function off() { - w.on(click, null); - } - w.on(click, function() { - d3_eventPreventDefault(); - off(); - }, true); - setTimeout(off, 0); - } - }; - } - d3.mouse = function(container) { - return d3_mousePoint(container, d3_eventSource()); - }; - function d3_mousePoint(container, e) { - if (e.changedTouches) e = e.changedTouches[0]; - var svg = container.ownerSVGElement || container; - if (svg.createSVGPoint) { - var point = svg.createSVGPoint(); - point.x = e.clientX, point.y = e.clientY; - point = point.matrixTransform(container.getScreenCTM().inverse()); - return [ point.x, point.y ]; - } - var rect = container.getBoundingClientRect(); - return [ e.clientX - rect.left - container.clientLeft, e.clientY - rect.top - container.clientTop ]; - } - d3.touches = function(container, touches) { - if (arguments.length < 2) touches = d3_eventSource().touches; - return touches ? d3_array(touches).map(function(touch) { - var point = d3_mousePoint(container, touch); - point.identifier = touch.identifier; - return point; - }) : []; - }; - d3.behavior.drag = function() { - var event = d3_eventDispatch(drag, "drag", "dragstart", "dragend"), origin = null, mousedown = dragstart(d3_noop, d3.mouse, d3_behavior_dragMouseSubject, "mousemove", "mouseup"), touchstart = dragstart(d3_behavior_dragTouchId, d3.touch, d3_behavior_dragTouchSubject, "touchmove", "touchend"); - function drag() { - this.on("mousedown.drag", mousedown).on("touchstart.drag", touchstart); - } - function dragstart(id, position, subject, move, end) { - return function() { - var that = this, target = d3.event.target, parent = that.parentNode, dispatch = event.of(that, arguments), dragged = 0, dragId = id(), dragName = ".drag" + (dragId == null ? "" : "-" + dragId), dragOffset, dragSubject = d3.select(subject()).on(move + dragName, moved).on(end + dragName, ended), dragRestore = d3_event_dragSuppress(), position0 = position(parent, dragId); - if (origin) { - dragOffset = origin.apply(that, arguments); - dragOffset = [ dragOffset.x - position0[0], dragOffset.y - position0[1] ]; - } else { - dragOffset = [ 0, 0 ]; - } - dispatch({ - type: "dragstart" - }); - function moved() { - var position1 = position(parent, dragId), dx, dy; - if (!position1) return; - dx = position1[0] - position0[0]; - dy = position1[1] - position0[1]; - dragged |= dx | dy; - position0 = position1; - dispatch({ - type: "drag", - x: position1[0] + dragOffset[0], - y: position1[1] + dragOffset[1], - dx: dx, - dy: dy - }); - } - function ended() { - if (!position(parent, dragId)) return; - dragSubject.on(move + dragName, null).on(end + dragName, null); - dragRestore(dragged && d3.event.target === target); - dispatch({ - type: "dragend" - }); - } - }; - } - drag.origin = function(x) { - if (!arguments.length) return origin; - origin = x; - return drag; - }; - return d3.rebind(drag, event, "on"); - }; - function d3_behavior_dragTouchId() { - return d3.event.changedTouches[0].identifier; - } - function d3_behavior_dragTouchSubject() { - return d3.event.target; - } - function d3_behavior_dragMouseSubject() { - return d3_window; - } - var π = Math.PI, τ = 2 * π, halfπ = π / 2, ε = 1e-6, ε2 = ε * ε, d3_radians = π / 180, d3_degrees = 180 / π; - function d3_sgn(x) { - return x > 0 ? 1 : x < 0 ? -1 : 0; - } - function d3_cross2d(a, b, c) { - return (b[0] - a[0]) * (c[1] - a[1]) - (b[1] - a[1]) * (c[0] - a[0]); - } - function d3_acos(x) { - return x > 1 ? 0 : x < -1 ? π : Math.acos(x); - } - function d3_asin(x) { - return x > 1 ? halfπ : x < -1 ? -halfπ : Math.asin(x); - } - function d3_sinh(x) { - return ((x = Math.exp(x)) - 1 / x) / 2; - } - function d3_cosh(x) { - return ((x = Math.exp(x)) + 1 / x) / 2; - } - function d3_tanh(x) { - return ((x = Math.exp(2 * x)) - 1) / (x + 1); - } - function d3_haversin(x) { - return (x = Math.sin(x / 2)) * x; - } - var ρ = Math.SQRT2, ρ2 = 2, ρ4 = 4; - d3.interpolateZoom = function(p0, p1) { - var ux0 = p0[0], uy0 = p0[1], w0 = p0[2], ux1 = p1[0], uy1 = p1[1], w1 = p1[2]; - var dx = ux1 - ux0, dy = uy1 - uy0, d2 = dx * dx + dy * dy, d1 = Math.sqrt(d2), b0 = (w1 * w1 - w0 * w0 + ρ4 * d2) / (2 * w0 * ρ2 * d1), b1 = (w1 * w1 - w0 * w0 - ρ4 * d2) / (2 * w1 * ρ2 * d1), r0 = Math.log(Math.sqrt(b0 * b0 + 1) - b0), r1 = Math.log(Math.sqrt(b1 * b1 + 1) - b1), dr = r1 - r0, S = (dr || Math.log(w1 / w0)) / ρ; - function interpolate(t) { - var s = t * S; - if (dr) { - var coshr0 = d3_cosh(r0), u = w0 / (ρ2 * d1) * (coshr0 * d3_tanh(ρ * s + r0) - d3_sinh(r0)); - return [ ux0 + u * dx, uy0 + u * dy, w0 * coshr0 / d3_cosh(ρ * s + r0) ]; - } - return [ ux0 + t * dx, uy0 + t * dy, w0 * Math.exp(ρ * s) ]; - } - interpolate.duration = S * 1e3; - return interpolate; - }; - d3.behavior.zoom = function() { - var view = { - x: 0, - y: 0, - k: 1 - }, translate0, center, size = [ 960, 500 ], scaleExtent = d3_behavior_zoomInfinity, mousedown = "mousedown.zoom", mousemove = "mousemove.zoom", mouseup = "mouseup.zoom", mousewheelTimer, touchstart = "touchstart.zoom", touchtime, event = d3_eventDispatch(zoom, "zoomstart", "zoom", "zoomend"), x0, x1, y0, y1; - function zoom(g) { - g.on(mousedown, mousedowned).on(d3_behavior_zoomWheel + ".zoom", mousewheeled).on(mousemove, mousewheelreset).on("dblclick.zoom", dblclicked).on(touchstart, touchstarted); - } - zoom.event = function(g) { - g.each(function() { - var dispatch = event.of(this, arguments), view1 = view; - if (d3_transitionInheritId) { - d3.select(this).transition().each("start.zoom", function() { - view = this.__chart__ || { - x: 0, - y: 0, - k: 1 - }; - zoomstarted(dispatch); - }).tween("zoom:zoom", function() { - var dx = size[0], dy = size[1], cx = dx / 2, cy = dy / 2, i = d3.interpolateZoom([ (cx - view.x) / view.k, (cy - view.y) / view.k, dx / view.k ], [ (cx - view1.x) / view1.k, (cy - view1.y) / view1.k, dx / view1.k ]); - return function(t) { - var l = i(t), k = dx / l[2]; - this.__chart__ = view = { - x: cx - l[0] * k, - y: cy - l[1] * k, - k: k - }; - zoomed(dispatch); - }; - }).each("end.zoom", function() { - zoomended(dispatch); - }); - } else { - this.__chart__ = view; - zoomstarted(dispatch); - zoomed(dispatch); - zoomended(dispatch); - } - }); - }; - zoom.translate = function(_) { - if (!arguments.length) return [ view.x, view.y ]; - view = { - x: +_[0], - y: +_[1], - k: view.k - }; - rescale(); - return zoom; - }; - zoom.scale = function(_) { - if (!arguments.length) return view.k; - view = { - x: view.x, - y: view.y, - k: +_ - }; - rescale(); - return zoom; - }; - zoom.scaleExtent = function(_) { - if (!arguments.length) return scaleExtent; - scaleExtent = _ == null ? d3_behavior_zoomInfinity : [ +_[0], +_[1] ]; - return zoom; - }; - zoom.center = function(_) { - if (!arguments.length) return center; - center = _ && [ +_[0], +_[1] ]; - return zoom; - }; - zoom.size = function(_) { - if (!arguments.length) return size; - size = _ && [ +_[0], +_[1] ]; - return zoom; - }; - zoom.x = function(z) { - if (!arguments.length) return x1; - x1 = z; - x0 = z.copy(); - view = { - x: 0, - y: 0, - k: 1 - }; - return zoom; - }; - zoom.y = function(z) { - if (!arguments.length) return y1; - y1 = z; - y0 = z.copy(); - view = { - x: 0, - y: 0, - k: 1 - }; - return zoom; - }; - function location(p) { - return [ (p[0] - view.x) / view.k, (p[1] - view.y) / view.k ]; - } - function point(l) { - return [ l[0] * view.k + view.x, l[1] * view.k + view.y ]; - } - function scaleTo(s) { - view.k = Math.max(scaleExtent[0], Math.min(scaleExtent[1], s)); - } - function translateTo(p, l) { - l = point(l); - view.x += p[0] - l[0]; - view.y += p[1] - l[1]; - } - function rescale() { - if (x1) x1.domain(x0.range().map(function(x) { - return (x - view.x) / view.k; - }).map(x0.invert)); - if (y1) y1.domain(y0.range().map(function(y) { - return (y - view.y) / view.k; - }).map(y0.invert)); - } - function zoomstarted(dispatch) { - dispatch({ - type: "zoomstart" - }); - } - function zoomed(dispatch) { - rescale(); - dispatch({ - type: "zoom", - scale: view.k, - translate: [ view.x, view.y ] - }); - } - function zoomended(dispatch) { - dispatch({ - type: "zoomend" - }); - } - function mousedowned() { - var that = this, target = d3.event.target, dispatch = event.of(that, arguments), dragged = 0, subject = d3.select(d3_window).on(mousemove, moved).on(mouseup, ended), location0 = location(d3.mouse(that)), dragRestore = d3_event_dragSuppress(); - d3_selection_interrupt.call(that); - zoomstarted(dispatch); - function moved() { - dragged = 1; - translateTo(d3.mouse(that), location0); - zoomed(dispatch); - } - function ended() { - subject.on(mousemove, d3_window === that ? mousewheelreset : null).on(mouseup, null); - dragRestore(dragged && d3.event.target === target); - zoomended(dispatch); - } - } - function touchstarted() { - var that = this, dispatch = event.of(that, arguments), locations0 = {}, distance0 = 0, scale0, zoomName = ".zoom-" + d3.event.changedTouches[0].identifier, touchmove = "touchmove" + zoomName, touchend = "touchend" + zoomName, targets = [], subject = d3.select(that).on(mousedown, null).on(touchstart, started), dragRestore = d3_event_dragSuppress(); - d3_selection_interrupt.call(that); - started(); - zoomstarted(dispatch); - function relocate() { - var touches = d3.touches(that); - scale0 = view.k; - touches.forEach(function(t) { - if (t.identifier in locations0) locations0[t.identifier] = location(t); - }); - return touches; - } - function started() { - var target = d3.event.target; - d3.select(target).on(touchmove, moved).on(touchend, ended); - targets.push(target); - var changed = d3.event.changedTouches; - for (var i = 0, n = changed.length; i < n; ++i) { - locations0[changed[i].identifier] = null; - } - var touches = relocate(), now = Date.now(); - if (touches.length === 1) { - if (now - touchtime < 500) { - var p = touches[0], l = locations0[p.identifier]; - scaleTo(view.k * 2); - translateTo(p, l); - d3_eventPreventDefault(); - zoomed(dispatch); - } - touchtime = now; - } else if (touches.length > 1) { - var p = touches[0], q = touches[1], dx = p[0] - q[0], dy = p[1] - q[1]; - distance0 = dx * dx + dy * dy; - } - } - function moved() { - var touches = d3.touches(that), p0, l0, p1, l1; - for (var i = 0, n = touches.length; i < n; ++i, l1 = null) { - p1 = touches[i]; - if (l1 = locations0[p1.identifier]) { - if (l0) break; - p0 = p1, l0 = l1; - } - } - if (l1) { - var distance1 = (distance1 = p1[0] - p0[0]) * distance1 + (distance1 = p1[1] - p0[1]) * distance1, scale1 = distance0 && Math.sqrt(distance1 / distance0); - p0 = [ (p0[0] + p1[0]) / 2, (p0[1] + p1[1]) / 2 ]; - l0 = [ (l0[0] + l1[0]) / 2, (l0[1] + l1[1]) / 2 ]; - scaleTo(scale1 * scale0); - } - touchtime = null; - translateTo(p0, l0); - zoomed(dispatch); - } - function ended() { - if (d3.event.touches.length) { - var changed = d3.event.changedTouches; - for (var i = 0, n = changed.length; i < n; ++i) { - delete locations0[changed[i].identifier]; - } - for (var identifier in locations0) { - return void relocate(); - } - } - d3.selectAll(targets).on(zoomName, null); - subject.on(mousedown, mousedowned).on(touchstart, touchstarted); - dragRestore(); - zoomended(dispatch); - } - } - function mousewheeled() { - var dispatch = event.of(this, arguments); - if (mousewheelTimer) clearTimeout(mousewheelTimer); else d3_selection_interrupt.call(this), - zoomstarted(dispatch); - mousewheelTimer = setTimeout(function() { - mousewheelTimer = null; - zoomended(dispatch); - }, 50); - d3_eventPreventDefault(); - var point = center || d3.mouse(this); - if (!translate0) translate0 = location(point); - scaleTo(Math.pow(2, d3_behavior_zoomDelta() * .002) * view.k); - translateTo(point, translate0); - zoomed(dispatch); - } - function mousewheelreset() { - translate0 = null; - } - function dblclicked() { - var dispatch = event.of(this, arguments), p = d3.mouse(this), l = location(p), k = Math.log(view.k) / Math.LN2; - zoomstarted(dispatch); - scaleTo(Math.pow(2, d3.event.shiftKey ? Math.ceil(k) - 1 : Math.floor(k) + 1)); - translateTo(p, l); - zoomed(dispatch); - zoomended(dispatch); - } - return d3.rebind(zoom, event, "on"); - }; - var d3_behavior_zoomInfinity = [ 0, Infinity ]; - var d3_behavior_zoomDelta, d3_behavior_zoomWheel = "onwheel" in d3_document ? (d3_behavior_zoomDelta = function() { - return -d3.event.deltaY * (d3.event.deltaMode ? 120 : 1); - }, "wheel") : "onmousewheel" in d3_document ? (d3_behavior_zoomDelta = function() { - return d3.event.wheelDelta; - }, "mousewheel") : (d3_behavior_zoomDelta = function() { - return -d3.event.detail; - }, "MozMousePixelScroll"); - function d3_Color() {} - d3_Color.prototype.toString = function() { - return this.rgb() + ""; - }; - d3.hsl = function(h, s, l) { - return arguments.length === 1 ? h instanceof d3_Hsl ? d3_hsl(h.h, h.s, h.l) : d3_rgb_parse("" + h, d3_rgb_hsl, d3_hsl) : d3_hsl(+h, +s, +l); - }; - function d3_hsl(h, s, l) { - return new d3_Hsl(h, s, l); - } - function d3_Hsl(h, s, l) { - this.h = h; - this.s = s; - this.l = l; - } - var d3_hslPrototype = d3_Hsl.prototype = new d3_Color(); - d3_hslPrototype.brighter = function(k) { - k = Math.pow(.7, arguments.length ? k : 1); - return d3_hsl(this.h, this.s, this.l / k); - }; - d3_hslPrototype.darker = function(k) { - k = Math.pow(.7, arguments.length ? k : 1); - return d3_hsl(this.h, this.s, k * this.l); - }; - d3_hslPrototype.rgb = function() { - return d3_hsl_rgb(this.h, this.s, this.l); - }; - function d3_hsl_rgb(h, s, l) { - var m1, m2; - h = isNaN(h) ? 0 : (h %= 360) < 0 ? h + 360 : h; - s = isNaN(s) ? 0 : s < 0 ? 0 : s > 1 ? 1 : s; - l = l < 0 ? 0 : l > 1 ? 1 : l; - m2 = l <= .5 ? l * (1 + s) : l + s - l * s; - m1 = 2 * l - m2; - function v(h) { - if (h > 360) h -= 360; else if (h < 0) h += 360; - if (h < 60) return m1 + (m2 - m1) * h / 60; - if (h < 180) return m2; - if (h < 240) return m1 + (m2 - m1) * (240 - h) / 60; - return m1; - } - function vv(h) { - return Math.round(v(h) * 255); - } - return d3_rgb(vv(h + 120), vv(h), vv(h - 120)); - } - d3.hcl = function(h, c, l) { - return arguments.length === 1 ? h instanceof d3_Hcl ? d3_hcl(h.h, h.c, h.l) : h instanceof d3_Lab ? d3_lab_hcl(h.l, h.a, h.b) : d3_lab_hcl((h = d3_rgb_lab((h = d3.rgb(h)).r, h.g, h.b)).l, h.a, h.b) : d3_hcl(+h, +c, +l); - }; - function d3_hcl(h, c, l) { - return new d3_Hcl(h, c, l); - } - function d3_Hcl(h, c, l) { - this.h = h; - this.c = c; - this.l = l; - } - var d3_hclPrototype = d3_Hcl.prototype = new d3_Color(); - d3_hclPrototype.brighter = function(k) { - return d3_hcl(this.h, this.c, Math.min(100, this.l + d3_lab_K * (arguments.length ? k : 1))); - }; - d3_hclPrototype.darker = function(k) { - return d3_hcl(this.h, this.c, Math.max(0, this.l - d3_lab_K * (arguments.length ? k : 1))); - }; - d3_hclPrototype.rgb = function() { - return d3_hcl_lab(this.h, this.c, this.l).rgb(); - }; - function d3_hcl_lab(h, c, l) { - if (isNaN(h)) h = 0; - if (isNaN(c)) c = 0; - return d3_lab(l, Math.cos(h *= d3_radians) * c, Math.sin(h) * c); - } - d3.lab = function(l, a, b) { - return arguments.length === 1 ? l instanceof d3_Lab ? d3_lab(l.l, l.a, l.b) : l instanceof d3_Hcl ? d3_hcl_lab(l.l, l.c, l.h) : d3_rgb_lab((l = d3.rgb(l)).r, l.g, l.b) : d3_lab(+l, +a, +b); - }; - function d3_lab(l, a, b) { - return new d3_Lab(l, a, b); - } - function d3_Lab(l, a, b) { - this.l = l; - this.a = a; - this.b = b; - } - var d3_lab_K = 18; - var d3_lab_X = .95047, d3_lab_Y = 1, d3_lab_Z = 1.08883; - var d3_labPrototype = d3_Lab.prototype = new d3_Color(); - d3_labPrototype.brighter = function(k) { - return d3_lab(Math.min(100, this.l + d3_lab_K * (arguments.length ? k : 1)), this.a, this.b); - }; - d3_labPrototype.darker = function(k) { - return d3_lab(Math.max(0, this.l - d3_lab_K * (arguments.length ? k : 1)), this.a, this.b); - }; - d3_labPrototype.rgb = function() { - return d3_lab_rgb(this.l, this.a, this.b); - }; - function d3_lab_rgb(l, a, b) { - var y = (l + 16) / 116, x = y + a / 500, z = y - b / 200; - x = d3_lab_xyz(x) * d3_lab_X; - y = d3_lab_xyz(y) * d3_lab_Y; - z = d3_lab_xyz(z) * d3_lab_Z; - return d3_rgb(d3_xyz_rgb(3.2404542 * x - 1.5371385 * y - .4985314 * z), d3_xyz_rgb(-.969266 * x + 1.8760108 * y + .041556 * z), d3_xyz_rgb(.0556434 * x - .2040259 * y + 1.0572252 * z)); - } - function d3_lab_hcl(l, a, b) { - return l > 0 ? d3_hcl(Math.atan2(b, a) * d3_degrees, Math.sqrt(a * a + b * b), l) : d3_hcl(NaN, NaN, l); - } - function d3_lab_xyz(x) { - return x > .206893034 ? x * x * x : (x - 4 / 29) / 7.787037; - } - function d3_xyz_lab(x) { - return x > .008856 ? Math.pow(x, 1 / 3) : 7.787037 * x + 4 / 29; - } - function d3_xyz_rgb(r) { - return Math.round(255 * (r <= .00304 ? 12.92 * r : 1.055 * Math.pow(r, 1 / 2.4) - .055)); - } - d3.rgb = function(r, g, b) { - return arguments.length === 1 ? r instanceof d3_Rgb ? d3_rgb(r.r, r.g, r.b) : d3_rgb_parse("" + r, d3_rgb, d3_hsl_rgb) : d3_rgb(~~r, ~~g, ~~b); - }; - function d3_rgbNumber(value) { - return d3_rgb(value >> 16, value >> 8 & 255, value & 255); - } - function d3_rgbString(value) { - return d3_rgbNumber(value) + ""; - } - function d3_rgb(r, g, b) { - return new d3_Rgb(r, g, b); - } - function d3_Rgb(r, g, b) { - this.r = r; - this.g = g; - this.b = b; - } - var d3_rgbPrototype = d3_Rgb.prototype = new d3_Color(); - d3_rgbPrototype.brighter = function(k) { - k = Math.pow(.7, arguments.length ? k : 1); - var r = this.r, g = this.g, b = this.b, i = 30; - if (!r && !g && !b) return d3_rgb(i, i, i); - if (r && r < i) r = i; - if (g && g < i) g = i; - if (b && b < i) b = i; - return d3_rgb(Math.min(255, ~~(r / k)), Math.min(255, ~~(g / k)), Math.min(255, ~~(b / k))); - }; - d3_rgbPrototype.darker = function(k) { - k = Math.pow(.7, arguments.length ? k : 1); - return d3_rgb(~~(k * this.r), ~~(k * this.g), ~~(k * this.b)); - }; - d3_rgbPrototype.hsl = function() { - return d3_rgb_hsl(this.r, this.g, this.b); - }; - d3_rgbPrototype.toString = function() { - return "#" + d3_rgb_hex(this.r) + d3_rgb_hex(this.g) + d3_rgb_hex(this.b); - }; - function d3_rgb_hex(v) { - return v < 16 ? "0" + Math.max(0, v).toString(16) : Math.min(255, v).toString(16); - } - function d3_rgb_parse(format, rgb, hsl) { - var r = 0, g = 0, b = 0, m1, m2, color; - m1 = /([a-z]+)\((.*)\)/i.exec(format); - if (m1) { - m2 = m1[2].split(","); - switch (m1[1]) { - case "hsl": - { - return hsl(parseFloat(m2[0]), parseFloat(m2[1]) / 100, parseFloat(m2[2]) / 100); - } - - case "rgb": - { - return rgb(d3_rgb_parseNumber(m2[0]), d3_rgb_parseNumber(m2[1]), d3_rgb_parseNumber(m2[2])); - } - } - } - if (color = d3_rgb_names.get(format)) return rgb(color.r, color.g, color.b); - if (format != null && format.charAt(0) === "#" && !isNaN(color = parseInt(format.substring(1), 16))) { - if (format.length === 4) { - r = (color & 3840) >> 4; - r = r >> 4 | r; - g = color & 240; - g = g >> 4 | g; - b = color & 15; - b = b << 4 | b; - } else if (format.length === 7) { - r = (color & 16711680) >> 16; - g = (color & 65280) >> 8; - b = color & 255; - } - } - return rgb(r, g, b); - } - function d3_rgb_hsl(r, g, b) { - var min = Math.min(r /= 255, g /= 255, b /= 255), max = Math.max(r, g, b), d = max - min, h, s, l = (max + min) / 2; - if (d) { - s = l < .5 ? d / (max + min) : d / (2 - max - min); - if (r == max) h = (g - b) / d + (g < b ? 6 : 0); else if (g == max) h = (b - r) / d + 2; else h = (r - g) / d + 4; - h *= 60; - } else { - h = NaN; - s = l > 0 && l < 1 ? 0 : h; - } - return d3_hsl(h, s, l); - } - function d3_rgb_lab(r, g, b) { - r = d3_rgb_xyz(r); - g = d3_rgb_xyz(g); - b = d3_rgb_xyz(b); - var x = d3_xyz_lab((.4124564 * r + .3575761 * g + .1804375 * b) / d3_lab_X), y = d3_xyz_lab((.2126729 * r + .7151522 * g + .072175 * b) / d3_lab_Y), z = d3_xyz_lab((.0193339 * r + .119192 * g + .9503041 * b) / d3_lab_Z); - return d3_lab(116 * y - 16, 500 * (x - y), 200 * (y - z)); - } - function d3_rgb_xyz(r) { - return (r /= 255) <= .04045 ? r / 12.92 : Math.pow((r + .055) / 1.055, 2.4); - } - function d3_rgb_parseNumber(c) { - var f = parseFloat(c); - return c.charAt(c.length - 1) === "%" ? Math.round(f * 2.55) : f; - } - var d3_rgb_names = d3.map({ - aliceblue: 15792383, - antiquewhite: 16444375, - aqua: 65535, - aquamarine: 8388564, - azure: 15794175, - beige: 16119260, - bisque: 16770244, - black: 0, - blanchedalmond: 16772045, - blue: 255, - blueviolet: 9055202, - brown: 10824234, - burlywood: 14596231, - cadetblue: 6266528, - chartreuse: 8388352, - chocolate: 13789470, - coral: 16744272, - cornflowerblue: 6591981, - cornsilk: 16775388, - crimson: 14423100, - cyan: 65535, - darkblue: 139, - darkcyan: 35723, - darkgoldenrod: 12092939, - darkgray: 11119017, - darkgreen: 25600, - darkgrey: 11119017, - darkkhaki: 12433259, - darkmagenta: 9109643, - darkolivegreen: 5597999, - darkorange: 16747520, - darkorchid: 10040012, - darkred: 9109504, - darksalmon: 15308410, - darkseagreen: 9419919, - darkslateblue: 4734347, - darkslategray: 3100495, - darkslategrey: 3100495, - darkturquoise: 52945, - darkviolet: 9699539, - deeppink: 16716947, - deepskyblue: 49151, - dimgray: 6908265, - dimgrey: 6908265, - dodgerblue: 2003199, - firebrick: 11674146, - floralwhite: 16775920, - forestgreen: 2263842, - fuchsia: 16711935, - gainsboro: 14474460, - ghostwhite: 16316671, - gold: 16766720, - goldenrod: 14329120, - gray: 8421504, - green: 32768, - greenyellow: 11403055, - grey: 8421504, - honeydew: 15794160, - hotpink: 16738740, - indianred: 13458524, - indigo: 4915330, - ivory: 16777200, - khaki: 15787660, - lavender: 15132410, - lavenderblush: 16773365, - lawngreen: 8190976, - lemonchiffon: 16775885, - lightblue: 11393254, - lightcoral: 15761536, - lightcyan: 14745599, - lightgoldenrodyellow: 16448210, - lightgray: 13882323, - lightgreen: 9498256, - lightgrey: 13882323, - lightpink: 16758465, - lightsalmon: 16752762, - lightseagreen: 2142890, - lightskyblue: 8900346, - lightslategray: 7833753, - lightslategrey: 7833753, - lightsteelblue: 11584734, - lightyellow: 16777184, - lime: 65280, - limegreen: 3329330, - linen: 16445670, - magenta: 16711935, - maroon: 8388608, - mediumaquamarine: 6737322, - mediumblue: 205, - mediumorchid: 12211667, - mediumpurple: 9662683, - mediumseagreen: 3978097, - mediumslateblue: 8087790, - mediumspringgreen: 64154, - mediumturquoise: 4772300, - mediumvioletred: 13047173, - midnightblue: 1644912, - mintcream: 16121850, - mistyrose: 16770273, - moccasin: 16770229, - navajowhite: 16768685, - navy: 128, - oldlace: 16643558, - olive: 8421376, - olivedrab: 7048739, - orange: 16753920, - orangered: 16729344, - orchid: 14315734, - palegoldenrod: 15657130, - palegreen: 10025880, - paleturquoise: 11529966, - palevioletred: 14381203, - papayawhip: 16773077, - peachpuff: 16767673, - peru: 13468991, - pink: 16761035, - plum: 14524637, - powderblue: 11591910, - purple: 8388736, - red: 16711680, - rosybrown: 12357519, - royalblue: 4286945, - saddlebrown: 9127187, - salmon: 16416882, - sandybrown: 16032864, - seagreen: 3050327, - seashell: 16774638, - sienna: 10506797, - silver: 12632256, - skyblue: 8900331, - slateblue: 6970061, - slategray: 7372944, - slategrey: 7372944, - snow: 16775930, - springgreen: 65407, - steelblue: 4620980, - tan: 13808780, - teal: 32896, - thistle: 14204888, - tomato: 16737095, - turquoise: 4251856, - violet: 15631086, - wheat: 16113331, - white: 16777215, - whitesmoke: 16119285, - yellow: 16776960, - yellowgreen: 10145074 - }); - d3_rgb_names.forEach(function(key, value) { - d3_rgb_names.set(key, d3_rgbNumber(value)); - }); - function d3_functor(v) { - return typeof v === "function" ? v : function() { - return v; - }; - } - d3.functor = d3_functor; - function d3_identity(d) { - return d; - } - d3.xhr = d3_xhrType(d3_identity); - function d3_xhrType(response) { - return function(url, mimeType, callback) { - if (arguments.length === 2 && typeof mimeType === "function") callback = mimeType, - mimeType = null; - return d3_xhr(url, mimeType, response, callback); - }; - } - function d3_xhr(url, mimeType, response, callback) { - var xhr = {}, dispatch = d3.dispatch("beforesend", "progress", "load", "error"), headers = {}, request = new XMLHttpRequest(), responseType = null; - if (d3_window.XDomainRequest && !("withCredentials" in request) && /^(http(s)?:)?\/\//.test(url)) request = new XDomainRequest(); - "onload" in request ? request.onload = request.onerror = respond : request.onreadystatechange = function() { - request.readyState > 3 && respond(); - }; - function respond() { - var status = request.status, result; - if (!status && request.responseText || status >= 200 && status < 300 || status === 304) { - try { - result = response.call(xhr, request); - } catch (e) { - dispatch.error.call(xhr, e); - return; - } - dispatch.load.call(xhr, result); - } else { - dispatch.error.call(xhr, request); - } - } - request.onprogress = function(event) { - var o = d3.event; - d3.event = event; - try { - dispatch.progress.call(xhr, request); - } finally { - d3.event = o; - } - }; - xhr.header = function(name, value) { - name = (name + "").toLowerCase(); - if (arguments.length < 2) return headers[name]; - if (value == null) delete headers[name]; else headers[name] = value + ""; - return xhr; - }; - xhr.mimeType = function(value) { - if (!arguments.length) return mimeType; - mimeType = value == null ? null : value + ""; - return xhr; - }; - xhr.responseType = function(value) { - if (!arguments.length) return responseType; - responseType = value; - return xhr; - }; - xhr.response = function(value) { - response = value; - return xhr; - }; - [ "get", "post" ].forEach(function(method) { - xhr[method] = function() { - return xhr.send.apply(xhr, [ method ].concat(d3_array(arguments))); - }; - }); - xhr.send = function(method, data, callback) { - if (arguments.length === 2 && typeof data === "function") callback = data, data = null; - request.open(method, url, true); - if (mimeType != null && !("accept" in headers)) headers["accept"] = mimeType + ",*/*"; - if (request.setRequestHeader) for (var name in headers) request.setRequestHeader(name, headers[name]); - if (mimeType != null && request.overrideMimeType) request.overrideMimeType(mimeType); - if (responseType != null) request.responseType = responseType; - if (callback != null) xhr.on("error", callback).on("load", function(request) { - callback(null, request); - }); - dispatch.beforesend.call(xhr, request); - request.send(data == null ? null : data); - return xhr; - }; - xhr.abort = function() { - request.abort(); - return xhr; - }; - d3.rebind(xhr, dispatch, "on"); - return callback == null ? xhr : xhr.get(d3_xhr_fixCallback(callback)); - } - function d3_xhr_fixCallback(callback) { - return callback.length === 1 ? function(error, request) { - callback(error == null ? request : null); - } : callback; - } - d3.dsv = function(delimiter, mimeType) { - var reFormat = new RegExp('["' + delimiter + "\n]"), delimiterCode = delimiter.charCodeAt(0); - function dsv(url, row, callback) { - if (arguments.length < 3) callback = row, row = null; - var xhr = d3_xhr(url, mimeType, row == null ? response : typedResponse(row), callback); - xhr.row = function(_) { - return arguments.length ? xhr.response((row = _) == null ? response : typedResponse(_)) : row; - }; - return xhr; - } - function response(request) { - return dsv.parse(request.responseText); - } - function typedResponse(f) { - return function(request) { - return dsv.parse(request.responseText, f); - }; - } - dsv.parse = function(text, f) { - var o; - return dsv.parseRows(text, function(row, i) { - if (o) return o(row, i - 1); - var a = new Function("d", "return {" + row.map(function(name, i) { - return JSON.stringify(name) + ": d[" + i + "]"; - }).join(",") + "}"); - o = f ? function(row, i) { - return f(a(row), i); - } : a; - }); - }; - dsv.parseRows = function(text, f) { - var EOL = {}, EOF = {}, rows = [], N = text.length, I = 0, n = 0, t, eol; - function token() { - if (I >= N) return EOF; - if (eol) return eol = false, EOL; - var j = I; - if (text.charCodeAt(j) === 34) { - var i = j; - while (i++ < N) { - if (text.charCodeAt(i) === 34) { - if (text.charCodeAt(i + 1) !== 34) break; - ++i; - } - } - I = i + 2; - var c = text.charCodeAt(i + 1); - if (c === 13) { - eol = true; - if (text.charCodeAt(i + 2) === 10) ++I; - } else if (c === 10) { - eol = true; - } - return text.substring(j + 1, i).replace(/""/g, '"'); - } - while (I < N) { - var c = text.charCodeAt(I++), k = 1; - if (c === 10) eol = true; else if (c === 13) { - eol = true; - if (text.charCodeAt(I) === 10) ++I, ++k; - } else if (c !== delimiterCode) continue; - return text.substring(j, I - k); - } - return text.substring(j); - } - while ((t = token()) !== EOF) { - var a = []; - while (t !== EOL && t !== EOF) { - a.push(t); - t = token(); - } - if (f && !(a = f(a, n++))) continue; - rows.push(a); - } - return rows; - }; - dsv.format = function(rows) { - if (Array.isArray(rows[0])) return dsv.formatRows(rows); - var fieldSet = new d3_Set(), fields = []; - rows.forEach(function(row) { - for (var field in row) { - if (!fieldSet.has(field)) { - fields.push(fieldSet.add(field)); - } - } - }); - return [ fields.map(formatValue).join(delimiter) ].concat(rows.map(function(row) { - return fields.map(function(field) { - return formatValue(row[field]); - }).join(delimiter); - })).join("\n"); - }; - dsv.formatRows = function(rows) { - return rows.map(formatRow).join("\n"); - }; - function formatRow(row) { - return row.map(formatValue).join(delimiter); - } - function formatValue(text) { - return reFormat.test(text) ? '"' + text.replace(/\"/g, '""') + '"' : text; - } - return dsv; - }; - d3.csv = d3.dsv(",", "text/csv"); - d3.tsv = d3.dsv(" ", "text/tab-separated-values"); - d3.touch = function(container, touches, identifier) { - if (arguments.length < 3) identifier = touches, touches = d3_eventSource().changedTouches; - if (touches) for (var i = 0, n = touches.length, touch; i < n; ++i) { - if ((touch = touches[i]).identifier === identifier) { - return d3_mousePoint(container, touch); - } - } - }; - var d3_timer_queueHead, d3_timer_queueTail, d3_timer_interval, d3_timer_timeout, d3_timer_active, d3_timer_frame = d3_window[d3_vendorSymbol(d3_window, "requestAnimationFrame")] || function(callback) { - setTimeout(callback, 17); - }; - d3.timer = function(callback, delay, then) { - var n = arguments.length; - if (n < 2) delay = 0; - if (n < 3) then = Date.now(); - var time = then + delay, timer = { - c: callback, - t: time, - f: false, - n: null - }; - if (d3_timer_queueTail) d3_timer_queueTail.n = timer; else d3_timer_queueHead = timer; - d3_timer_queueTail = timer; - if (!d3_timer_interval) { - d3_timer_timeout = clearTimeout(d3_timer_timeout); - d3_timer_interval = 1; - d3_timer_frame(d3_timer_step); - } - }; - function d3_timer_step() { - var now = d3_timer_mark(), delay = d3_timer_sweep() - now; - if (delay > 24) { - if (isFinite(delay)) { - clearTimeout(d3_timer_timeout); - d3_timer_timeout = setTimeout(d3_timer_step, delay); - } - d3_timer_interval = 0; - } else { - d3_timer_interval = 1; - d3_timer_frame(d3_timer_step); - } - } - d3.timer.flush = function() { - d3_timer_mark(); - d3_timer_sweep(); - }; - function d3_timer_mark() { - var now = Date.now(); - d3_timer_active = d3_timer_queueHead; - while (d3_timer_active) { - if (now >= d3_timer_active.t) d3_timer_active.f = d3_timer_active.c(now - d3_timer_active.t); - d3_timer_active = d3_timer_active.n; - } - return now; - } - function d3_timer_sweep() { - var t0, t1 = d3_timer_queueHead, time = Infinity; - while (t1) { - if (t1.f) { - t1 = t0 ? t0.n = t1.n : d3_timer_queueHead = t1.n; - } else { - if (t1.t < time) time = t1.t; - t1 = (t0 = t1).n; - } - } - d3_timer_queueTail = t0; - return time; - } - function d3_format_precision(x, p) { - return p - (x ? Math.ceil(Math.log(x) / Math.LN10) : 1); - } - d3.round = function(x, n) { - return n ? Math.round(x * (n = Math.pow(10, n))) / n : Math.round(x); - }; - var d3_formatPrefixes = [ "y", "z", "a", "f", "p", "n", "µ", "m", "", "k", "M", "G", "T", "P", "E", "Z", "Y" ].map(d3_formatPrefix); - d3.formatPrefix = function(value, precision) { - var i = 0; - if (value) { - if (value < 0) value *= -1; - if (precision) value = d3.round(value, d3_format_precision(value, precision)); - i = 1 + Math.floor(1e-12 + Math.log(value) / Math.LN10); - i = Math.max(-24, Math.min(24, Math.floor((i - 1) / 3) * 3)); - } - return d3_formatPrefixes[8 + i / 3]; - }; - function d3_formatPrefix(d, i) { - var k = Math.pow(10, abs(8 - i) * 3); - return { - scale: i > 8 ? function(d) { - return d / k; - } : function(d) { - return d * k; - }, - symbol: d - }; - } - function d3_locale_numberFormat(locale) { - var locale_decimal = locale.decimal, locale_thousands = locale.thousands, locale_grouping = locale.grouping, locale_currency = locale.currency, formatGroup = locale_grouping ? function(value) { - var i = value.length, t = [], j = 0, g = locale_grouping[0]; - while (i > 0 && g > 0) { - t.push(value.substring(i -= g, i + g)); - g = locale_grouping[j = (j + 1) % locale_grouping.length]; - } - return t.reverse().join(locale_thousands); - } : d3_identity; - return function(specifier) { - var match = d3_format_re.exec(specifier), fill = match[1] || " ", align = match[2] || ">", sign = match[3] || "", symbol = match[4] || "", zfill = match[5], width = +match[6], comma = match[7], precision = match[8], type = match[9], scale = 1, prefix = "", suffix = "", integer = false; - if (precision) precision = +precision.substring(1); - if (zfill || fill === "0" && align === "=") { - zfill = fill = "0"; - align = "="; - if (comma) width -= Math.floor((width - 1) / 4); - } - switch (type) { - case "n": - comma = true; - type = "g"; - break; - - case "%": - scale = 100; - suffix = "%"; - type = "f"; - break; - - case "p": - scale = 100; - suffix = "%"; - type = "r"; - break; - - case "b": - case "o": - case "x": - case "X": - if (symbol === "#") prefix = "0" + type.toLowerCase(); - - case "c": - case "d": - integer = true; - precision = 0; - break; - - case "s": - scale = -1; - type = "r"; - break; - } - if (symbol === "$") prefix = locale_currency[0], suffix = locale_currency[1]; - if (type == "r" && !precision) type = "g"; - if (precision != null) { - if (type == "g") precision = Math.max(1, Math.min(21, precision)); else if (type == "e" || type == "f") precision = Math.max(0, Math.min(20, precision)); - } - type = d3_format_types.get(type) || d3_format_typeDefault; - var zcomma = zfill && comma; - return function(value) { - var fullSuffix = suffix; - if (integer && value % 1) return ""; - var negative = value < 0 || value === 0 && 1 / value < 0 ? (value = -value, "-") : sign; - if (scale < 0) { - var unit = d3.formatPrefix(value, precision); - value = unit.scale(value); - fullSuffix = unit.symbol + suffix; - } else { - value *= scale; - } - value = type(value, precision); - var i = value.lastIndexOf("."), before = i < 0 ? value : value.substring(0, i), after = i < 0 ? "" : locale_decimal + value.substring(i + 1); - if (!zfill && comma) before = formatGroup(before); - var length = prefix.length + before.length + after.length + (zcomma ? 0 : negative.length), padding = length < width ? new Array(length = width - length + 1).join(fill) : ""; - if (zcomma) before = formatGroup(padding + before); - negative += prefix; - value = before + after; - return (align === "<" ? negative + value + padding : align === ">" ? padding + negative + value : align === "^" ? padding.substring(0, length >>= 1) + negative + value + padding.substring(length) : negative + (zcomma ? value : padding + value)) + fullSuffix; - }; - }; - } - var d3_format_re = /(?:([^{])?([<>=^]))?([+\- ])?([$#])?(0)?(\d+)?(,)?(\.-?\d+)?([a-z%])?/i; - var d3_format_types = d3.map({ - b: function(x) { - return x.toString(2); - }, - c: function(x) { - return String.fromCharCode(x); - }, - o: function(x) { - return x.toString(8); - }, - x: function(x) { - return x.toString(16); - }, - X: function(x) { - return x.toString(16).toUpperCase(); - }, - g: function(x, p) { - return x.toPrecision(p); - }, - e: function(x, p) { - return x.toExponential(p); - }, - f: function(x, p) { - return x.toFixed(p); - }, - r: function(x, p) { - return (x = d3.round(x, d3_format_precision(x, p))).toFixed(Math.max(0, Math.min(20, d3_format_precision(x * (1 + 1e-15), p)))); - } - }); - function d3_format_typeDefault(x) { - return x + ""; - } - var d3_time = d3.time = {}, d3_date = Date; - function d3_date_utc() { - this._ = new Date(arguments.length > 1 ? Date.UTC.apply(this, arguments) : arguments[0]); - } - d3_date_utc.prototype = { - getDate: function() { - return this._.getUTCDate(); - }, - getDay: function() { - return this._.getUTCDay(); - }, - getFullYear: function() { - return this._.getUTCFullYear(); - }, - getHours: function() { - return this._.getUTCHours(); - }, - getMilliseconds: function() { - return this._.getUTCMilliseconds(); - }, - getMinutes: function() { - return this._.getUTCMinutes(); - }, - getMonth: function() { - return this._.getUTCMonth(); - }, - getSeconds: function() { - return this._.getUTCSeconds(); - }, - getTime: function() { - return this._.getTime(); - }, - getTimezoneOffset: function() { - return 0; - }, - valueOf: function() { - return this._.valueOf(); - }, - setDate: function() { - d3_time_prototype.setUTCDate.apply(this._, arguments); - }, - setDay: function() { - d3_time_prototype.setUTCDay.apply(this._, arguments); - }, - setFullYear: function() { - d3_time_prototype.setUTCFullYear.apply(this._, arguments); - }, - setHours: function() { - d3_time_prototype.setUTCHours.apply(this._, arguments); - }, - setMilliseconds: function() { - d3_time_prototype.setUTCMilliseconds.apply(this._, arguments); - }, - setMinutes: function() { - d3_time_prototype.setUTCMinutes.apply(this._, arguments); - }, - setMonth: function() { - d3_time_prototype.setUTCMonth.apply(this._, arguments); - }, - setSeconds: function() { - d3_time_prototype.setUTCSeconds.apply(this._, arguments); - }, - setTime: function() { - d3_time_prototype.setTime.apply(this._, arguments); - } - }; - var d3_time_prototype = Date.prototype; - function d3_time_interval(local, step, number) { - function round(date) { - var d0 = local(date), d1 = offset(d0, 1); - return date - d0 < d1 - date ? d0 : d1; - } - function ceil(date) { - step(date = local(new d3_date(date - 1)), 1); - return date; - } - function offset(date, k) { - step(date = new d3_date(+date), k); - return date; - } - function range(t0, t1, dt) { - var time = ceil(t0), times = []; - if (dt > 1) { - while (time < t1) { - if (!(number(time) % dt)) times.push(new Date(+time)); - step(time, 1); - } - } else { - while (time < t1) times.push(new Date(+time)), step(time, 1); - } - return times; - } - function range_utc(t0, t1, dt) { - try { - d3_date = d3_date_utc; - var utc = new d3_date_utc(); - utc._ = t0; - return range(utc, t1, dt); - } finally { - d3_date = Date; - } - } - local.floor = local; - local.round = round; - local.ceil = ceil; - local.offset = offset; - local.range = range; - var utc = local.utc = d3_time_interval_utc(local); - utc.floor = utc; - utc.round = d3_time_interval_utc(round); - utc.ceil = d3_time_interval_utc(ceil); - utc.offset = d3_time_interval_utc(offset); - utc.range = range_utc; - return local; - } - function d3_time_interval_utc(method) { - return function(date, k) { - try { - d3_date = d3_date_utc; - var utc = new d3_date_utc(); - utc._ = date; - return method(utc, k)._; - } finally { - d3_date = Date; - } - }; - } - d3_time.year = d3_time_interval(function(date) { - date = d3_time.day(date); - date.setMonth(0, 1); - return date; - }, function(date, offset) { - date.setFullYear(date.getFullYear() + offset); - }, function(date) { - return date.getFullYear(); - }); - d3_time.years = d3_time.year.range; - d3_time.years.utc = d3_time.year.utc.range; - d3_time.day = d3_time_interval(function(date) { - var day = new d3_date(2e3, 0); - day.setFullYear(date.getFullYear(), date.getMonth(), date.getDate()); - return day; - }, function(date, offset) { - date.setDate(date.getDate() + offset); - }, function(date) { - return date.getDate() - 1; - }); - d3_time.days = d3_time.day.range; - d3_time.days.utc = d3_time.day.utc.range; - d3_time.dayOfYear = function(date) { - var year = d3_time.year(date); - return Math.floor((date - year - (date.getTimezoneOffset() - year.getTimezoneOffset()) * 6e4) / 864e5); - }; - [ "sunday", "monday", "tuesday", "wednesday", "thursday", "friday", "saturday" ].forEach(function(day, i) { - i = 7 - i; - var interval = d3_time[day] = d3_time_interval(function(date) { - (date = d3_time.day(date)).setDate(date.getDate() - (date.getDay() + i) % 7); - return date; - }, function(date, offset) { - date.setDate(date.getDate() + Math.floor(offset) * 7); - }, function(date) { - var day = d3_time.year(date).getDay(); - return Math.floor((d3_time.dayOfYear(date) + (day + i) % 7) / 7) - (day !== i); - }); - d3_time[day + "s"] = interval.range; - d3_time[day + "s"].utc = interval.utc.range; - d3_time[day + "OfYear"] = function(date) { - var day = d3_time.year(date).getDay(); - return Math.floor((d3_time.dayOfYear(date) + (day + i) % 7) / 7); - }; - }); - d3_time.week = d3_time.sunday; - d3_time.weeks = d3_time.sunday.range; - d3_time.weeks.utc = d3_time.sunday.utc.range; - d3_time.weekOfYear = d3_time.sundayOfYear; - function d3_locale_timeFormat(locale) { - var locale_dateTime = locale.dateTime, locale_date = locale.date, locale_time = locale.time, locale_periods = locale.periods, locale_days = locale.days, locale_shortDays = locale.shortDays, locale_months = locale.months, locale_shortMonths = locale.shortMonths; - function d3_time_format(template) { - var n = template.length; - function format(date) { - var string = [], i = -1, j = 0, c, p, f; - while (++i < n) { - if (template.charCodeAt(i) === 37) { - string.push(template.substring(j, i)); - if ((p = d3_time_formatPads[c = template.charAt(++i)]) != null) c = template.charAt(++i); - if (f = d3_time_formats[c]) c = f(date, p == null ? c === "e" ? " " : "0" : p); - string.push(c); - j = i + 1; - } - } - string.push(template.substring(j, i)); - return string.join(""); - } - format.parse = function(string) { - var d = { - y: 1900, - m: 0, - d: 1, - H: 0, - M: 0, - S: 0, - L: 0, - Z: null - }, i = d3_time_parse(d, template, string, 0); - if (i != string.length) return null; - if ("p" in d) d.H = d.H % 12 + d.p * 12; - var localZ = d.Z != null && d3_date !== d3_date_utc, date = new (localZ ? d3_date_utc : d3_date)(); - if ("j" in d) date.setFullYear(d.y, 0, d.j); else if ("w" in d && ("W" in d || "U" in d)) { - date.setFullYear(d.y, 0, 1); - date.setFullYear(d.y, 0, "W" in d ? (d.w + 6) % 7 + d.W * 7 - (date.getDay() + 5) % 7 : d.w + d.U * 7 - (date.getDay() + 6) % 7); - } else date.setFullYear(d.y, d.m, d.d); - date.setHours(d.H + Math.floor(d.Z / 100), d.M + d.Z % 100, d.S, d.L); - return localZ ? date._ : date; - }; - format.toString = function() { - return template; - }; - return format; - } - function d3_time_parse(date, template, string, j) { - var c, p, t, i = 0, n = template.length, m = string.length; - while (i < n) { - if (j >= m) return -1; - c = template.charCodeAt(i++); - if (c === 37) { - t = template.charAt(i++); - p = d3_time_parsers[t in d3_time_formatPads ? template.charAt(i++) : t]; - if (!p || (j = p(date, string, j)) < 0) return -1; - } else if (c != string.charCodeAt(j++)) { - return -1; - } - } - return j; - } - d3_time_format.utc = function(template) { - var local = d3_time_format(template); - function format(date) { - try { - d3_date = d3_date_utc; - var utc = new d3_date(); - utc._ = date; - return local(utc); - } finally { - d3_date = Date; - } - } - format.parse = function(string) { - try { - d3_date = d3_date_utc; - var date = local.parse(string); - return date && date._; - } finally { - d3_date = Date; - } - }; - format.toString = local.toString; - return format; - }; - d3_time_format.multi = d3_time_format.utc.multi = d3_time_formatMulti; - var d3_time_periodLookup = d3.map(), d3_time_dayRe = d3_time_formatRe(locale_days), d3_time_dayLookup = d3_time_formatLookup(locale_days), d3_time_dayAbbrevRe = d3_time_formatRe(locale_shortDays), d3_time_dayAbbrevLookup = d3_time_formatLookup(locale_shortDays), d3_time_monthRe = d3_time_formatRe(locale_months), d3_time_monthLookup = d3_time_formatLookup(locale_months), d3_time_monthAbbrevRe = d3_time_formatRe(locale_shortMonths), d3_time_monthAbbrevLookup = d3_time_formatLookup(locale_shortMonths); - locale_periods.forEach(function(p, i) { - d3_time_periodLookup.set(p.toLowerCase(), i); - }); - var d3_time_formats = { - a: function(d) { - return locale_shortDays[d.getDay()]; - }, - A: function(d) { - return locale_days[d.getDay()]; - }, - b: function(d) { - return locale_shortMonths[d.getMonth()]; - }, - B: function(d) { - return locale_months[d.getMonth()]; - }, - c: d3_time_format(locale_dateTime), - d: function(d, p) { - return d3_time_formatPad(d.getDate(), p, 2); - }, - e: function(d, p) { - return d3_time_formatPad(d.getDate(), p, 2); - }, - H: function(d, p) { - return d3_time_formatPad(d.getHours(), p, 2); - }, - I: function(d, p) { - return d3_time_formatPad(d.getHours() % 12 || 12, p, 2); - }, - j: function(d, p) { - return d3_time_formatPad(1 + d3_time.dayOfYear(d), p, 3); - }, - L: function(d, p) { - return d3_time_formatPad(d.getMilliseconds(), p, 3); - }, - m: function(d, p) { - return d3_time_formatPad(d.getMonth() + 1, p, 2); - }, - M: function(d, p) { - return d3_time_formatPad(d.getMinutes(), p, 2); - }, - p: function(d) { - return locale_periods[+(d.getHours() >= 12)]; - }, - S: function(d, p) { - return d3_time_formatPad(d.getSeconds(), p, 2); - }, - U: function(d, p) { - return d3_time_formatPad(d3_time.sundayOfYear(d), p, 2); - }, - w: function(d) { - return d.getDay(); - }, - W: function(d, p) { - return d3_time_formatPad(d3_time.mondayOfYear(d), p, 2); - }, - x: d3_time_format(locale_date), - X: d3_time_format(locale_time), - y: function(d, p) { - return d3_time_formatPad(d.getFullYear() % 100, p, 2); - }, - Y: function(d, p) { - return d3_time_formatPad(d.getFullYear() % 1e4, p, 4); - }, - Z: d3_time_zone, - "%": function() { - return "%"; - } - }; - var d3_time_parsers = { - a: d3_time_parseWeekdayAbbrev, - A: d3_time_parseWeekday, - b: d3_time_parseMonthAbbrev, - B: d3_time_parseMonth, - c: d3_time_parseLocaleFull, - d: d3_time_parseDay, - e: d3_time_parseDay, - H: d3_time_parseHour24, - I: d3_time_parseHour24, - j: d3_time_parseDayOfYear, - L: d3_time_parseMilliseconds, - m: d3_time_parseMonthNumber, - M: d3_time_parseMinutes, - p: d3_time_parseAmPm, - S: d3_time_parseSeconds, - U: d3_time_parseWeekNumberSunday, - w: d3_time_parseWeekdayNumber, - W: d3_time_parseWeekNumberMonday, - x: d3_time_parseLocaleDate, - X: d3_time_parseLocaleTime, - y: d3_time_parseYear, - Y: d3_time_parseFullYear, - Z: d3_time_parseZone, - "%": d3_time_parseLiteralPercent - }; - function d3_time_parseWeekdayAbbrev(date, string, i) { - d3_time_dayAbbrevRe.lastIndex = 0; - var n = d3_time_dayAbbrevRe.exec(string.substring(i)); - return n ? (date.w = d3_time_dayAbbrevLookup.get(n[0].toLowerCase()), i + n[0].length) : -1; - } - function d3_time_parseWeekday(date, string, i) { - d3_time_dayRe.lastIndex = 0; - var n = d3_time_dayRe.exec(string.substring(i)); - return n ? (date.w = d3_time_dayLookup.get(n[0].toLowerCase()), i + n[0].length) : -1; - } - function d3_time_parseMonthAbbrev(date, string, i) { - d3_time_monthAbbrevRe.lastIndex = 0; - var n = d3_time_monthAbbrevRe.exec(string.substring(i)); - return n ? (date.m = d3_time_monthAbbrevLookup.get(n[0].toLowerCase()), i + n[0].length) : -1; - } - function d3_time_parseMonth(date, string, i) { - d3_time_monthRe.lastIndex = 0; - var n = d3_time_monthRe.exec(string.substring(i)); - return n ? (date.m = d3_time_monthLookup.get(n[0].toLowerCase()), i + n[0].length) : -1; - } - function d3_time_parseLocaleFull(date, string, i) { - return d3_time_parse(date, d3_time_formats.c.toString(), string, i); - } - function d3_time_parseLocaleDate(date, string, i) { - return d3_time_parse(date, d3_time_formats.x.toString(), string, i); - } - function d3_time_parseLocaleTime(date, string, i) { - return d3_time_parse(date, d3_time_formats.X.toString(), string, i); - } - function d3_time_parseAmPm(date, string, i) { - var n = d3_time_periodLookup.get(string.substring(i, i += 2).toLowerCase()); - return n == null ? -1 : (date.p = n, i); - } - return d3_time_format; - } - var d3_time_formatPads = { - "-": "", - _: " ", - "0": "0" - }, d3_time_numberRe = /^\s*\d+/, d3_time_percentRe = /^%/; - function d3_time_formatPad(value, fill, width) { - var sign = value < 0 ? "-" : "", string = (sign ? -value : value) + "", length = string.length; - return sign + (length < width ? new Array(width - length + 1).join(fill) + string : string); - } - function d3_time_formatRe(names) { - return new RegExp("^(?:" + names.map(d3.requote).join("|") + ")", "i"); - } - function d3_time_formatLookup(names) { - var map = new d3_Map(), i = -1, n = names.length; - while (++i < n) map.set(names[i].toLowerCase(), i); - return map; - } - function d3_time_parseWeekdayNumber(date, string, i) { - d3_time_numberRe.lastIndex = 0; - var n = d3_time_numberRe.exec(string.substring(i, i + 1)); - return n ? (date.w = +n[0], i + n[0].length) : -1; - } - function d3_time_parseWeekNumberSunday(date, string, i) { - d3_time_numberRe.lastIndex = 0; - var n = d3_time_numberRe.exec(string.substring(i)); - return n ? (date.U = +n[0], i + n[0].length) : -1; - } - function d3_time_parseWeekNumberMonday(date, string, i) { - d3_time_numberRe.lastIndex = 0; - var n = d3_time_numberRe.exec(string.substring(i)); - return n ? (date.W = +n[0], i + n[0].length) : -1; - } - function d3_time_parseFullYear(date, string, i) { - d3_time_numberRe.lastIndex = 0; - var n = d3_time_numberRe.exec(string.substring(i, i + 4)); - return n ? (date.y = +n[0], i + n[0].length) : -1; - } - function d3_time_parseYear(date, string, i) { - d3_time_numberRe.lastIndex = 0; - var n = d3_time_numberRe.exec(string.substring(i, i + 2)); - return n ? (date.y = d3_time_expandYear(+n[0]), i + n[0].length) : -1; - } - function d3_time_parseZone(date, string, i) { - return /^[+-]\d{4}$/.test(string = string.substring(i, i + 5)) ? (date.Z = -string, - i + 5) : -1; - } - function d3_time_expandYear(d) { - return d + (d > 68 ? 1900 : 2e3); - } - function d3_time_parseMonthNumber(date, string, i) { - d3_time_numberRe.lastIndex = 0; - var n = d3_time_numberRe.exec(string.substring(i, i + 2)); - return n ? (date.m = n[0] - 1, i + n[0].length) : -1; - } - function d3_time_parseDay(date, string, i) { - d3_time_numberRe.lastIndex = 0; - var n = d3_time_numberRe.exec(string.substring(i, i + 2)); - return n ? (date.d = +n[0], i + n[0].length) : -1; - } - function d3_time_parseDayOfYear(date, string, i) { - d3_time_numberRe.lastIndex = 0; - var n = d3_time_numberRe.exec(string.substring(i, i + 3)); - return n ? (date.j = +n[0], i + n[0].length) : -1; - } - function d3_time_parseHour24(date, string, i) { - d3_time_numberRe.lastIndex = 0; - var n = d3_time_numberRe.exec(string.substring(i, i + 2)); - return n ? (date.H = +n[0], i + n[0].length) : -1; - } - function d3_time_parseMinutes(date, string, i) { - d3_time_numberRe.lastIndex = 0; - var n = d3_time_numberRe.exec(string.substring(i, i + 2)); - return n ? (date.M = +n[0], i + n[0].length) : -1; - } - function d3_time_parseSeconds(date, string, i) { - d3_time_numberRe.lastIndex = 0; - var n = d3_time_numberRe.exec(string.substring(i, i + 2)); - return n ? (date.S = +n[0], i + n[0].length) : -1; - } - function d3_time_parseMilliseconds(date, string, i) { - d3_time_numberRe.lastIndex = 0; - var n = d3_time_numberRe.exec(string.substring(i, i + 3)); - return n ? (date.L = +n[0], i + n[0].length) : -1; - } - function d3_time_zone(d) { - var z = d.getTimezoneOffset(), zs = z > 0 ? "-" : "+", zh = ~~(abs(z) / 60), zm = abs(z) % 60; - return zs + d3_time_formatPad(zh, "0", 2) + d3_time_formatPad(zm, "0", 2); - } - function d3_time_parseLiteralPercent(date, string, i) { - d3_time_percentRe.lastIndex = 0; - var n = d3_time_percentRe.exec(string.substring(i, i + 1)); - return n ? i + n[0].length : -1; - } - function d3_time_formatMulti(formats) { - var n = formats.length, i = -1; - while (++i < n) formats[i][0] = this(formats[i][0]); - return function(date) { - var i = 0, f = formats[i]; - while (!f[1](date)) f = formats[++i]; - return f[0](date); - }; - } - d3.locale = function(locale) { - return { - numberFormat: d3_locale_numberFormat(locale), - timeFormat: d3_locale_timeFormat(locale) - }; - }; - var d3_locale_enUS = d3.locale({ - decimal: ".", - thousands: ",", - grouping: [ 3 ], - currency: [ "$", "" ], - dateTime: "%a %b %e %X %Y", - date: "%m/%d/%Y", - time: "%H:%M:%S", - periods: [ "AM", "PM" ], - days: [ "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" ], - shortDays: [ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" ], - months: [ "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" ], - shortMonths: [ "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" ] - }); - d3.format = d3_locale_enUS.numberFormat; - d3.geo = {}; - function d3_adder() {} - d3_adder.prototype = { - s: 0, - t: 0, - add: function(y) { - d3_adderSum(y, this.t, d3_adderTemp); - d3_adderSum(d3_adderTemp.s, this.s, this); - if (this.s) this.t += d3_adderTemp.t; else this.s = d3_adderTemp.t; - }, - reset: function() { - this.s = this.t = 0; - }, - valueOf: function() { - return this.s; - } - }; - var d3_adderTemp = new d3_adder(); - function d3_adderSum(a, b, o) { - var x = o.s = a + b, bv = x - a, av = x - bv; - o.t = a - av + (b - bv); - } - d3.geo.stream = function(object, listener) { - if (object && d3_geo_streamObjectType.hasOwnProperty(object.type)) { - d3_geo_streamObjectType[object.type](object, listener); - } else { - d3_geo_streamGeometry(object, listener); - } - }; - function d3_geo_streamGeometry(geometry, listener) { - if (geometry && d3_geo_streamGeometryType.hasOwnProperty(geometry.type)) { - d3_geo_streamGeometryType[geometry.type](geometry, listener); - } - } - var d3_geo_streamObjectType = { - Feature: function(feature, listener) { - d3_geo_streamGeometry(feature.geometry, listener); - }, - FeatureCollection: function(object, listener) { - var features = object.features, i = -1, n = features.length; - while (++i < n) d3_geo_streamGeometry(features[i].geometry, listener); - } - }; - var d3_geo_streamGeometryType = { - Sphere: function(object, listener) { - listener.sphere(); - }, - Point: function(object, listener) { - object = object.coordinates; - listener.point(object[0], object[1], object[2]); - }, - MultiPoint: function(object, listener) { - var coordinates = object.coordinates, i = -1, n = coordinates.length; - while (++i < n) object = coordinates[i], listener.point(object[0], object[1], object[2]); - }, - LineString: function(object, listener) { - d3_geo_streamLine(object.coordinates, listener, 0); - }, - MultiLineString: function(object, listener) { - var coordinates = object.coordinates, i = -1, n = coordinates.length; - while (++i < n) d3_geo_streamLine(coordinates[i], listener, 0); - }, - Polygon: function(object, listener) { - d3_geo_streamPolygon(object.coordinates, listener); - }, - MultiPolygon: function(object, listener) { - var coordinates = object.coordinates, i = -1, n = coordinates.length; - while (++i < n) d3_geo_streamPolygon(coordinates[i], listener); - }, - GeometryCollection: function(object, listener) { - var geometries = object.geometries, i = -1, n = geometries.length; - while (++i < n) d3_geo_streamGeometry(geometries[i], listener); - } - }; - function d3_geo_streamLine(coordinates, listener, closed) { - var i = -1, n = coordinates.length - closed, coordinate; - listener.lineStart(); - while (++i < n) coordinate = coordinates[i], listener.point(coordinate[0], coordinate[1], coordinate[2]); - listener.lineEnd(); - } - function d3_geo_streamPolygon(coordinates, listener) { - var i = -1, n = coordinates.length; - listener.polygonStart(); - while (++i < n) d3_geo_streamLine(coordinates[i], listener, 1); - listener.polygonEnd(); - } - d3.geo.area = function(object) { - d3_geo_areaSum = 0; - d3.geo.stream(object, d3_geo_area); - return d3_geo_areaSum; - }; - var d3_geo_areaSum, d3_geo_areaRingSum = new d3_adder(); - var d3_geo_area = { - sphere: function() { - d3_geo_areaSum += 4 * π; - }, - point: d3_noop, - lineStart: d3_noop, - lineEnd: d3_noop, - polygonStart: function() { - d3_geo_areaRingSum.reset(); - d3_geo_area.lineStart = d3_geo_areaRingStart; - }, - polygonEnd: function() { - var area = 2 * d3_geo_areaRingSum; - d3_geo_areaSum += area < 0 ? 4 * π + area : area; - d3_geo_area.lineStart = d3_geo_area.lineEnd = d3_geo_area.point = d3_noop; - } - }; - function d3_geo_areaRingStart() { - var λ00, φ00, λ0, cosφ0, sinφ0; - d3_geo_area.point = function(λ, φ) { - d3_geo_area.point = nextPoint; - λ0 = (λ00 = λ) * d3_radians, cosφ0 = Math.cos(φ = (φ00 = φ) * d3_radians / 2 + π / 4), - sinφ0 = Math.sin(φ); - }; - function nextPoint(λ, φ) { - λ *= d3_radians; - φ = φ * d3_radians / 2 + π / 4; - var dλ = λ - λ0, sdλ = dλ >= 0 ? 1 : -1, adλ = sdλ * dλ, cosφ = Math.cos(φ), sinφ = Math.sin(φ), k = sinφ0 * sinφ, u = cosφ0 * cosφ + k * Math.cos(adλ), v = k * sdλ * Math.sin(adλ); - d3_geo_areaRingSum.add(Math.atan2(v, u)); - λ0 = λ, cosφ0 = cosφ, sinφ0 = sinφ; - } - d3_geo_area.lineEnd = function() { - nextPoint(λ00, φ00); - }; - } - function d3_geo_cartesian(spherical) { - var λ = spherical[0], φ = spherical[1], cosφ = Math.cos(φ); - return [ cosφ * Math.cos(λ), cosφ * Math.sin(λ), Math.sin(φ) ]; - } - function d3_geo_cartesianDot(a, b) { - return a[0] * b[0] + a[1] * b[1] + a[2] * b[2]; - } - function d3_geo_cartesianCross(a, b) { - return [ a[1] * b[2] - a[2] * b[1], a[2] * b[0] - a[0] * b[2], a[0] * b[1] - a[1] * b[0] ]; - } - function d3_geo_cartesianAdd(a, b) { - a[0] += b[0]; - a[1] += b[1]; - a[2] += b[2]; - } - function d3_geo_cartesianScale(vector, k) { - return [ vector[0] * k, vector[1] * k, vector[2] * k ]; - } - function d3_geo_cartesianNormalize(d) { - var l = Math.sqrt(d[0] * d[0] + d[1] * d[1] + d[2] * d[2]); - d[0] /= l; - d[1] /= l; - d[2] /= l; - } - function d3_geo_spherical(cartesian) { - return [ Math.atan2(cartesian[1], cartesian[0]), d3_asin(cartesian[2]) ]; - } - function d3_geo_sphericalEqual(a, b) { - return abs(a[0] - b[0]) < ε && abs(a[1] - b[1]) < ε; - } - d3.geo.bounds = function() { - var λ0, φ0, λ1, φ1, λ_, λ__, φ__, p0, dλSum, ranges, range; - var bound = { - point: point, - lineStart: lineStart, - lineEnd: lineEnd, - polygonStart: function() { - bound.point = ringPoint; - bound.lineStart = ringStart; - bound.lineEnd = ringEnd; - dλSum = 0; - d3_geo_area.polygonStart(); - }, - polygonEnd: function() { - d3_geo_area.polygonEnd(); - bound.point = point; - bound.lineStart = lineStart; - bound.lineEnd = lineEnd; - if (d3_geo_areaRingSum < 0) λ0 = -(λ1 = 180), φ0 = -(φ1 = 90); else if (dλSum > ε) φ1 = 90; else if (dλSum < -ε) φ0 = -90; - range[0] = λ0, range[1] = λ1; - } - }; - function point(λ, φ) { - ranges.push(range = [ λ0 = λ, λ1 = λ ]); - if (φ < φ0) φ0 = φ; - if (φ > φ1) φ1 = φ; - } - function linePoint(λ, φ) { - var p = d3_geo_cartesian([ λ * d3_radians, φ * d3_radians ]); - if (p0) { - var normal = d3_geo_cartesianCross(p0, p), equatorial = [ normal[1], -normal[0], 0 ], inflection = d3_geo_cartesianCross(equatorial, normal); - d3_geo_cartesianNormalize(inflection); - inflection = d3_geo_spherical(inflection); - var dλ = λ - λ_, s = dλ > 0 ? 1 : -1, λi = inflection[0] * d3_degrees * s, antimeridian = abs(dλ) > 180; - if (antimeridian ^ (s * λ_ < λi && λi < s * λ)) { - var φi = inflection[1] * d3_degrees; - if (φi > φ1) φ1 = φi; - } else if (λi = (λi + 360) % 360 - 180, antimeridian ^ (s * λ_ < λi && λi < s * λ)) { - var φi = -inflection[1] * d3_degrees; - if (φi < φ0) φ0 = φi; - } else { - if (φ < φ0) φ0 = φ; - if (φ > φ1) φ1 = φ; - } - if (antimeridian) { - if (λ < λ_) { - if (angle(λ0, λ) > angle(λ0, λ1)) λ1 = λ; - } else { - if (angle(λ, λ1) > angle(λ0, λ1)) λ0 = λ; - } - } else { - if (λ1 >= λ0) { - if (λ < λ0) λ0 = λ; - if (λ > λ1) λ1 = λ; - } else { - if (λ > λ_) { - if (angle(λ0, λ) > angle(λ0, λ1)) λ1 = λ; - } else { - if (angle(λ, λ1) > angle(λ0, λ1)) λ0 = λ; - } - } - } - } else { - point(λ, φ); - } - p0 = p, λ_ = λ; - } - function lineStart() { - bound.point = linePoint; - } - function lineEnd() { - range[0] = λ0, range[1] = λ1; - bound.point = point; - p0 = null; - } - function ringPoint(λ, φ) { - if (p0) { - var dλ = λ - λ_; - dλSum += abs(dλ) > 180 ? dλ + (dλ > 0 ? 360 : -360) : dλ; - } else λ__ = λ, φ__ = φ; - d3_geo_area.point(λ, φ); - linePoint(λ, φ); - } - function ringStart() { - d3_geo_area.lineStart(); - } - function ringEnd() { - ringPoint(λ__, φ__); - d3_geo_area.lineEnd(); - if (abs(dλSum) > ε) λ0 = -(λ1 = 180); - range[0] = λ0, range[1] = λ1; - p0 = null; - } - function angle(λ0, λ1) { - return (λ1 -= λ0) < 0 ? λ1 + 360 : λ1; - } - function compareRanges(a, b) { - return a[0] - b[0]; - } - function withinRange(x, range) { - return range[0] <= range[1] ? range[0] <= x && x <= range[1] : x < range[0] || range[1] < x; - } - return function(feature) { - φ1 = λ1 = -(λ0 = φ0 = Infinity); - ranges = []; - d3.geo.stream(feature, bound); - var n = ranges.length; - if (n) { - ranges.sort(compareRanges); - for (var i = 1, a = ranges[0], b, merged = [ a ]; i < n; ++i) { - b = ranges[i]; - if (withinRange(b[0], a) || withinRange(b[1], a)) { - if (angle(a[0], b[1]) > angle(a[0], a[1])) a[1] = b[1]; - if (angle(b[0], a[1]) > angle(a[0], a[1])) a[0] = b[0]; - } else { - merged.push(a = b); - } - } - var best = -Infinity, dλ; - for (var n = merged.length - 1, i = 0, a = merged[n], b; i <= n; a = b, ++i) { - b = merged[i]; - if ((dλ = angle(a[1], b[0])) > best) best = dλ, λ0 = b[0], λ1 = a[1]; - } - } - ranges = range = null; - return λ0 === Infinity || φ0 === Infinity ? [ [ NaN, NaN ], [ NaN, NaN ] ] : [ [ λ0, φ0 ], [ λ1, φ1 ] ]; - }; - }(); - d3.geo.centroid = function(object) { - d3_geo_centroidW0 = d3_geo_centroidW1 = d3_geo_centroidX0 = d3_geo_centroidY0 = d3_geo_centroidZ0 = d3_geo_centroidX1 = d3_geo_centroidY1 = d3_geo_centroidZ1 = d3_geo_centroidX2 = d3_geo_centroidY2 = d3_geo_centroidZ2 = 0; - d3.geo.stream(object, d3_geo_centroid); - var x = d3_geo_centroidX2, y = d3_geo_centroidY2, z = d3_geo_centroidZ2, m = x * x + y * y + z * z; - if (m < ε2) { - x = d3_geo_centroidX1, y = d3_geo_centroidY1, z = d3_geo_centroidZ1; - if (d3_geo_centroidW1 < ε) x = d3_geo_centroidX0, y = d3_geo_centroidY0, z = d3_geo_centroidZ0; - m = x * x + y * y + z * z; - if (m < ε2) return [ NaN, NaN ]; - } - return [ Math.atan2(y, x) * d3_degrees, d3_asin(z / Math.sqrt(m)) * d3_degrees ]; - }; - var d3_geo_centroidW0, d3_geo_centroidW1, d3_geo_centroidX0, d3_geo_centroidY0, d3_geo_centroidZ0, d3_geo_centroidX1, d3_geo_centroidY1, d3_geo_centroidZ1, d3_geo_centroidX2, d3_geo_centroidY2, d3_geo_centroidZ2; - var d3_geo_centroid = { - sphere: d3_noop, - point: d3_geo_centroidPoint, - lineStart: d3_geo_centroidLineStart, - lineEnd: d3_geo_centroidLineEnd, - polygonStart: function() { - d3_geo_centroid.lineStart = d3_geo_centroidRingStart; - }, - polygonEnd: function() { - d3_geo_centroid.lineStart = d3_geo_centroidLineStart; - } - }; - function d3_geo_centroidPoint(λ, φ) { - λ *= d3_radians; - var cosφ = Math.cos(φ *= d3_radians); - d3_geo_centroidPointXYZ(cosφ * Math.cos(λ), cosφ * Math.sin(λ), Math.sin(φ)); - } - function d3_geo_centroidPointXYZ(x, y, z) { - ++d3_geo_centroidW0; - d3_geo_centroidX0 += (x - d3_geo_centroidX0) / d3_geo_centroidW0; - d3_geo_centroidY0 += (y - d3_geo_centroidY0) / d3_geo_centroidW0; - d3_geo_centroidZ0 += (z - d3_geo_centroidZ0) / d3_geo_centroidW0; - } - function d3_geo_centroidLineStart() { - var x0, y0, z0; - d3_geo_centroid.point = function(λ, φ) { - λ *= d3_radians; - var cosφ = Math.cos(φ *= d3_radians); - x0 = cosφ * Math.cos(λ); - y0 = cosφ * Math.sin(λ); - z0 = Math.sin(φ); - d3_geo_centroid.point = nextPoint; - d3_geo_centroidPointXYZ(x0, y0, z0); - }; - function nextPoint(λ, φ) { - λ *= d3_radians; - var cosφ = Math.cos(φ *= d3_radians), x = cosφ * Math.cos(λ), y = cosφ * Math.sin(λ), z = Math.sin(φ), w = Math.atan2(Math.sqrt((w = y0 * z - z0 * y) * w + (w = z0 * x - x0 * z) * w + (w = x0 * y - y0 * x) * w), x0 * x + y0 * y + z0 * z); - d3_geo_centroidW1 += w; - d3_geo_centroidX1 += w * (x0 + (x0 = x)); - d3_geo_centroidY1 += w * (y0 + (y0 = y)); - d3_geo_centroidZ1 += w * (z0 + (z0 = z)); - d3_geo_centroidPointXYZ(x0, y0, z0); - } - } - function d3_geo_centroidLineEnd() { - d3_geo_centroid.point = d3_geo_centroidPoint; - } - function d3_geo_centroidRingStart() { - var λ00, φ00, x0, y0, z0; - d3_geo_centroid.point = function(λ, φ) { - λ00 = λ, φ00 = φ; - d3_geo_centroid.point = nextPoint; - λ *= d3_radians; - var cosφ = Math.cos(φ *= d3_radians); - x0 = cosφ * Math.cos(λ); - y0 = cosφ * Math.sin(λ); - z0 = Math.sin(φ); - d3_geo_centroidPointXYZ(x0, y0, z0); - }; - d3_geo_centroid.lineEnd = function() { - nextPoint(λ00, φ00); - d3_geo_centroid.lineEnd = d3_geo_centroidLineEnd; - d3_geo_centroid.point = d3_geo_centroidPoint; - }; - function nextPoint(λ, φ) { - λ *= d3_radians; - var cosφ = Math.cos(φ *= d3_radians), x = cosφ * Math.cos(λ), y = cosφ * Math.sin(λ), z = Math.sin(φ), cx = y0 * z - z0 * y, cy = z0 * x - x0 * z, cz = x0 * y - y0 * x, m = Math.sqrt(cx * cx + cy * cy + cz * cz), u = x0 * x + y0 * y + z0 * z, v = m && -d3_acos(u) / m, w = Math.atan2(m, u); - d3_geo_centroidX2 += v * cx; - d3_geo_centroidY2 += v * cy; - d3_geo_centroidZ2 += v * cz; - d3_geo_centroidW1 += w; - d3_geo_centroidX1 += w * (x0 + (x0 = x)); - d3_geo_centroidY1 += w * (y0 + (y0 = y)); - d3_geo_centroidZ1 += w * (z0 + (z0 = z)); - d3_geo_centroidPointXYZ(x0, y0, z0); - } - } - function d3_true() { - return true; - } - function d3_geo_clipPolygon(segments, compare, clipStartInside, interpolate, listener) { - var subject = [], clip = []; - segments.forEach(function(segment) { - if ((n = segment.length - 1) <= 0) return; - var n, p0 = segment[0], p1 = segment[n]; - if (d3_geo_sphericalEqual(p0, p1)) { - listener.lineStart(); - for (var i = 0; i < n; ++i) listener.point((p0 = segment[i])[0], p0[1]); - listener.lineEnd(); - return; - } - var a = new d3_geo_clipPolygonIntersection(p0, segment, null, true), b = new d3_geo_clipPolygonIntersection(p0, null, a, false); - a.o = b; - subject.push(a); - clip.push(b); - a = new d3_geo_clipPolygonIntersection(p1, segment, null, false); - b = new d3_geo_clipPolygonIntersection(p1, null, a, true); - a.o = b; - subject.push(a); - clip.push(b); - }); - clip.sort(compare); - d3_geo_clipPolygonLinkCircular(subject); - d3_geo_clipPolygonLinkCircular(clip); - if (!subject.length) return; - for (var i = 0, entry = clipStartInside, n = clip.length; i < n; ++i) { - clip[i].e = entry = !entry; - } - var start = subject[0], points, point; - while (1) { - var current = start, isSubject = true; - while (current.v) if ((current = current.n) === start) return; - points = current.z; - listener.lineStart(); - do { - current.v = current.o.v = true; - if (current.e) { - if (isSubject) { - for (var i = 0, n = points.length; i < n; ++i) listener.point((point = points[i])[0], point[1]); - } else { - interpolate(current.x, current.n.x, 1, listener); - } - current = current.n; - } else { - if (isSubject) { - points = current.p.z; - for (var i = points.length - 1; i >= 0; --i) listener.point((point = points[i])[0], point[1]); - } else { - interpolate(current.x, current.p.x, -1, listener); - } - current = current.p; - } - current = current.o; - points = current.z; - isSubject = !isSubject; - } while (!current.v); - listener.lineEnd(); - } - } - function d3_geo_clipPolygonLinkCircular(array) { - if (!(n = array.length)) return; - var n, i = 0, a = array[0], b; - while (++i < n) { - a.n = b = array[i]; - b.p = a; - a = b; - } - a.n = b = array[0]; - b.p = a; - } - function d3_geo_clipPolygonIntersection(point, points, other, entry) { - this.x = point; - this.z = points; - this.o = other; - this.e = entry; - this.v = false; - this.n = this.p = null; - } - function d3_geo_clip(pointVisible, clipLine, interpolate, clipStart) { - return function(rotate, listener) { - var line = clipLine(listener), rotatedClipStart = rotate.invert(clipStart[0], clipStart[1]); - var clip = { - point: point, - lineStart: lineStart, - lineEnd: lineEnd, - polygonStart: function() { - clip.point = pointRing; - clip.lineStart = ringStart; - clip.lineEnd = ringEnd; - segments = []; - polygon = []; - }, - polygonEnd: function() { - clip.point = point; - clip.lineStart = lineStart; - clip.lineEnd = lineEnd; - segments = d3.merge(segments); - var clipStartInside = d3_geo_pointInPolygon(rotatedClipStart, polygon); - if (segments.length) { - if (!polygonStarted) listener.polygonStart(), polygonStarted = true; - d3_geo_clipPolygon(segments, d3_geo_clipSort, clipStartInside, interpolate, listener); - } else if (clipStartInside) { - if (!polygonStarted) listener.polygonStart(), polygonStarted = true; - listener.lineStart(); - interpolate(null, null, 1, listener); - listener.lineEnd(); - } - if (polygonStarted) listener.polygonEnd(), polygonStarted = false; - segments = polygon = null; - }, - sphere: function() { - listener.polygonStart(); - listener.lineStart(); - interpolate(null, null, 1, listener); - listener.lineEnd(); - listener.polygonEnd(); - } - }; - function point(λ, φ) { - var point = rotate(λ, φ); - if (pointVisible(λ = point[0], φ = point[1])) listener.point(λ, φ); - } - function pointLine(λ, φ) { - var point = rotate(λ, φ); - line.point(point[0], point[1]); - } - function lineStart() { - clip.point = pointLine; - line.lineStart(); - } - function lineEnd() { - clip.point = point; - line.lineEnd(); - } - var segments; - var buffer = d3_geo_clipBufferListener(), ringListener = clipLine(buffer), polygonStarted = false, polygon, ring; - function pointRing(λ, φ) { - ring.push([ λ, φ ]); - var point = rotate(λ, φ); - ringListener.point(point[0], point[1]); - } - function ringStart() { - ringListener.lineStart(); - ring = []; - } - function ringEnd() { - pointRing(ring[0][0], ring[0][1]); - ringListener.lineEnd(); - var clean = ringListener.clean(), ringSegments = buffer.buffer(), segment, n = ringSegments.length; - ring.pop(); - polygon.push(ring); - ring = null; - if (!n) return; - if (clean & 1) { - segment = ringSegments[0]; - var n = segment.length - 1, i = -1, point; - if (n > 0) { - if (!polygonStarted) listener.polygonStart(), polygonStarted = true; - listener.lineStart(); - while (++i < n) listener.point((point = segment[i])[0], point[1]); - listener.lineEnd(); - } - return; - } - if (n > 1 && clean & 2) ringSegments.push(ringSegments.pop().concat(ringSegments.shift())); - segments.push(ringSegments.filter(d3_geo_clipSegmentLength1)); - } - return clip; - }; - } - function d3_geo_clipSegmentLength1(segment) { - return segment.length > 1; - } - function d3_geo_clipBufferListener() { - var lines = [], line; - return { - lineStart: function() { - lines.push(line = []); - }, - point: function(λ, φ) { - line.push([ λ, φ ]); - }, - lineEnd: d3_noop, - buffer: function() { - var buffer = lines; - lines = []; - line = null; - return buffer; - }, - rejoin: function() { - if (lines.length > 1) lines.push(lines.pop().concat(lines.shift())); - } - }; - } - function d3_geo_clipSort(a, b) { - return ((a = a.x)[0] < 0 ? a[1] - halfπ - ε : halfπ - a[1]) - ((b = b.x)[0] < 0 ? b[1] - halfπ - ε : halfπ - b[1]); - } - function d3_geo_pointInPolygon(point, polygon) { - var meridian = point[0], parallel = point[1], meridianNormal = [ Math.sin(meridian), -Math.cos(meridian), 0 ], polarAngle = 0, winding = 0; - d3_geo_areaRingSum.reset(); - for (var i = 0, n = polygon.length; i < n; ++i) { - var ring = polygon[i], m = ring.length; - if (!m) continue; - var point0 = ring[0], λ0 = point0[0], φ0 = point0[1] / 2 + π / 4, sinφ0 = Math.sin(φ0), cosφ0 = Math.cos(φ0), j = 1; - while (true) { - if (j === m) j = 0; - point = ring[j]; - var λ = point[0], φ = point[1] / 2 + π / 4, sinφ = Math.sin(φ), cosφ = Math.cos(φ), dλ = λ - λ0, sdλ = dλ >= 0 ? 1 : -1, adλ = sdλ * dλ, antimeridian = adλ > π, k = sinφ0 * sinφ; - d3_geo_areaRingSum.add(Math.atan2(k * sdλ * Math.sin(adλ), cosφ0 * cosφ + k * Math.cos(adλ))); - polarAngle += antimeridian ? dλ + sdλ * τ : dλ; - if (antimeridian ^ λ0 >= meridian ^ λ >= meridian) { - var arc = d3_geo_cartesianCross(d3_geo_cartesian(point0), d3_geo_cartesian(point)); - d3_geo_cartesianNormalize(arc); - var intersection = d3_geo_cartesianCross(meridianNormal, arc); - d3_geo_cartesianNormalize(intersection); - var φarc = (antimeridian ^ dλ >= 0 ? -1 : 1) * d3_asin(intersection[2]); - if (parallel > φarc || parallel === φarc && (arc[0] || arc[1])) { - winding += antimeridian ^ dλ >= 0 ? 1 : -1; - } - } - if (!j++) break; - λ0 = λ, sinφ0 = sinφ, cosφ0 = cosφ, point0 = point; - } - } - return (polarAngle < -ε || polarAngle < ε && d3_geo_areaRingSum < 0) ^ winding & 1; - } - var d3_geo_clipAntimeridian = d3_geo_clip(d3_true, d3_geo_clipAntimeridianLine, d3_geo_clipAntimeridianInterpolate, [ -π, -π / 2 ]); - function d3_geo_clipAntimeridianLine(listener) { - var λ0 = NaN, φ0 = NaN, sλ0 = NaN, clean; - return { - lineStart: function() { - listener.lineStart(); - clean = 1; - }, - point: function(λ1, φ1) { - var sλ1 = λ1 > 0 ? π : -π, dλ = abs(λ1 - λ0); - if (abs(dλ - π) < ε) { - listener.point(λ0, φ0 = (φ0 + φ1) / 2 > 0 ? halfπ : -halfπ); - listener.point(sλ0, φ0); - listener.lineEnd(); - listener.lineStart(); - listener.point(sλ1, φ0); - listener.point(λ1, φ0); - clean = 0; - } else if (sλ0 !== sλ1 && dλ >= π) { - if (abs(λ0 - sλ0) < ε) λ0 -= sλ0 * ε; - if (abs(λ1 - sλ1) < ε) λ1 -= sλ1 * ε; - φ0 = d3_geo_clipAntimeridianIntersect(λ0, φ0, λ1, φ1); - listener.point(sλ0, φ0); - listener.lineEnd(); - listener.lineStart(); - listener.point(sλ1, φ0); - clean = 0; - } - listener.point(λ0 = λ1, φ0 = φ1); - sλ0 = sλ1; - }, - lineEnd: function() { - listener.lineEnd(); - λ0 = φ0 = NaN; - }, - clean: function() { - return 2 - clean; - } - }; - } - function d3_geo_clipAntimeridianIntersect(λ0, φ0, λ1, φ1) { - var cosφ0, cosφ1, sinλ0_λ1 = Math.sin(λ0 - λ1); - return abs(sinλ0_λ1) > ε ? Math.atan((Math.sin(φ0) * (cosφ1 = Math.cos(φ1)) * Math.sin(λ1) - Math.sin(φ1) * (cosφ0 = Math.cos(φ0)) * Math.sin(λ0)) / (cosφ0 * cosφ1 * sinλ0_λ1)) : (φ0 + φ1) / 2; - } - function d3_geo_clipAntimeridianInterpolate(from, to, direction, listener) { - var φ; - if (from == null) { - φ = direction * halfπ; - listener.point(-π, φ); - listener.point(0, φ); - listener.point(π, φ); - listener.point(π, 0); - listener.point(π, -φ); - listener.point(0, -φ); - listener.point(-π, -φ); - listener.point(-π, 0); - listener.point(-π, φ); - } else if (abs(from[0] - to[0]) > ε) { - var s = from[0] < to[0] ? π : -π; - φ = direction * s / 2; - listener.point(-s, φ); - listener.point(0, φ); - listener.point(s, φ); - } else { - listener.point(to[0], to[1]); - } - } - function d3_geo_clipCircle(radius) { - var cr = Math.cos(radius), smallRadius = cr > 0, notHemisphere = abs(cr) > ε, interpolate = d3_geo_circleInterpolate(radius, 6 * d3_radians); - return d3_geo_clip(visible, clipLine, interpolate, smallRadius ? [ 0, -radius ] : [ -π, radius - π ]); - function visible(λ, φ) { - return Math.cos(λ) * Math.cos(φ) > cr; - } - function clipLine(listener) { - var point0, c0, v0, v00, clean; - return { - lineStart: function() { - v00 = v0 = false; - clean = 1; - }, - point: function(λ, φ) { - var point1 = [ λ, φ ], point2, v = visible(λ, φ), c = smallRadius ? v ? 0 : code(λ, φ) : v ? code(λ + (λ < 0 ? π : -π), φ) : 0; - if (!point0 && (v00 = v0 = v)) listener.lineStart(); - if (v !== v0) { - point2 = intersect(point0, point1); - if (d3_geo_sphericalEqual(point0, point2) || d3_geo_sphericalEqual(point1, point2)) { - point1[0] += ε; - point1[1] += ε; - v = visible(point1[0], point1[1]); - } - } - if (v !== v0) { - clean = 0; - if (v) { - listener.lineStart(); - point2 = intersect(point1, point0); - listener.point(point2[0], point2[1]); - } else { - point2 = intersect(point0, point1); - listener.point(point2[0], point2[1]); - listener.lineEnd(); - } - point0 = point2; - } else if (notHemisphere && point0 && smallRadius ^ v) { - var t; - if (!(c & c0) && (t = intersect(point1, point0, true))) { - clean = 0; - if (smallRadius) { - listener.lineStart(); - listener.point(t[0][0], t[0][1]); - listener.point(t[1][0], t[1][1]); - listener.lineEnd(); - } else { - listener.point(t[1][0], t[1][1]); - listener.lineEnd(); - listener.lineStart(); - listener.point(t[0][0], t[0][1]); - } - } - } - if (v && (!point0 || !d3_geo_sphericalEqual(point0, point1))) { - listener.point(point1[0], point1[1]); - } - point0 = point1, v0 = v, c0 = c; - }, - lineEnd: function() { - if (v0) listener.lineEnd(); - point0 = null; - }, - clean: function() { - return clean | (v00 && v0) << 1; - } - }; - } - function intersect(a, b, two) { - var pa = d3_geo_cartesian(a), pb = d3_geo_cartesian(b); - var n1 = [ 1, 0, 0 ], n2 = d3_geo_cartesianCross(pa, pb), n2n2 = d3_geo_cartesianDot(n2, n2), n1n2 = n2[0], determinant = n2n2 - n1n2 * n1n2; - if (!determinant) return !two && a; - var c1 = cr * n2n2 / determinant, c2 = -cr * n1n2 / determinant, n1xn2 = d3_geo_cartesianCross(n1, n2), A = d3_geo_cartesianScale(n1, c1), B = d3_geo_cartesianScale(n2, c2); - d3_geo_cartesianAdd(A, B); - var u = n1xn2, w = d3_geo_cartesianDot(A, u), uu = d3_geo_cartesianDot(u, u), t2 = w * w - uu * (d3_geo_cartesianDot(A, A) - 1); - if (t2 < 0) return; - var t = Math.sqrt(t2), q = d3_geo_cartesianScale(u, (-w - t) / uu); - d3_geo_cartesianAdd(q, A); - q = d3_geo_spherical(q); - if (!two) return q; - var λ0 = a[0], λ1 = b[0], φ0 = a[1], φ1 = b[1], z; - if (λ1 < λ0) z = λ0, λ0 = λ1, λ1 = z; - var δλ = λ1 - λ0, polar = abs(δλ - π) < ε, meridian = polar || δλ < ε; - if (!polar && φ1 < φ0) z = φ0, φ0 = φ1, φ1 = z; - if (meridian ? polar ? φ0 + φ1 > 0 ^ q[1] < (abs(q[0] - λ0) < ε ? φ0 : φ1) : φ0 <= q[1] && q[1] <= φ1 : δλ > π ^ (λ0 <= q[0] && q[0] <= λ1)) { - var q1 = d3_geo_cartesianScale(u, (-w + t) / uu); - d3_geo_cartesianAdd(q1, A); - return [ q, d3_geo_spherical(q1) ]; - } - } - function code(λ, φ) { - var r = smallRadius ? radius : π - radius, code = 0; - if (λ < -r) code |= 1; else if (λ > r) code |= 2; - if (φ < -r) code |= 4; else if (φ > r) code |= 8; - return code; - } - } - function d3_geom_clipLine(x0, y0, x1, y1) { - return function(line) { - var a = line.a, b = line.b, ax = a.x, ay = a.y, bx = b.x, by = b.y, t0 = 0, t1 = 1, dx = bx - ax, dy = by - ay, r; - r = x0 - ax; - if (!dx && r > 0) return; - r /= dx; - if (dx < 0) { - if (r < t0) return; - if (r < t1) t1 = r; - } else if (dx > 0) { - if (r > t1) return; - if (r > t0) t0 = r; - } - r = x1 - ax; - if (!dx && r < 0) return; - r /= dx; - if (dx < 0) { - if (r > t1) return; - if (r > t0) t0 = r; - } else if (dx > 0) { - if (r < t0) return; - if (r < t1) t1 = r; - } - r = y0 - ay; - if (!dy && r > 0) return; - r /= dy; - if (dy < 0) { - if (r < t0) return; - if (r < t1) t1 = r; - } else if (dy > 0) { - if (r > t1) return; - if (r > t0) t0 = r; - } - r = y1 - ay; - if (!dy && r < 0) return; - r /= dy; - if (dy < 0) { - if (r > t1) return; - if (r > t0) t0 = r; - } else if (dy > 0) { - if (r < t0) return; - if (r < t1) t1 = r; - } - if (t0 > 0) line.a = { - x: ax + t0 * dx, - y: ay + t0 * dy - }; - if (t1 < 1) line.b = { - x: ax + t1 * dx, - y: ay + t1 * dy - }; - return line; - }; - } - var d3_geo_clipExtentMAX = 1e9; - d3.geo.clipExtent = function() { - var x0, y0, x1, y1, stream, clip, clipExtent = { - stream: function(output) { - if (stream) stream.valid = false; - stream = clip(output); - stream.valid = true; - return stream; - }, - extent: function(_) { - if (!arguments.length) return [ [ x0, y0 ], [ x1, y1 ] ]; - clip = d3_geo_clipExtent(x0 = +_[0][0], y0 = +_[0][1], x1 = +_[1][0], y1 = +_[1][1]); - if (stream) stream.valid = false, stream = null; - return clipExtent; - } - }; - return clipExtent.extent([ [ 0, 0 ], [ 960, 500 ] ]); - }; - function d3_geo_clipExtent(x0, y0, x1, y1) { - return function(listener) { - var listener_ = listener, bufferListener = d3_geo_clipBufferListener(), clipLine = d3_geom_clipLine(x0, y0, x1, y1), segments, polygon, ring; - var clip = { - point: point, - lineStart: lineStart, - lineEnd: lineEnd, - polygonStart: function() { - listener = bufferListener; - segments = []; - polygon = []; - clean = true; - }, - polygonEnd: function() { - listener = listener_; - segments = d3.merge(segments); - var clipStartInside = insidePolygon([ x0, y1 ]), inside = clean && clipStartInside, visible = segments.length; - if (inside || visible) { - listener.polygonStart(); - if (inside) { - listener.lineStart(); - interpolate(null, null, 1, listener); - listener.lineEnd(); - } - if (visible) { - d3_geo_clipPolygon(segments, compare, clipStartInside, interpolate, listener); - } - listener.polygonEnd(); - } - segments = polygon = ring = null; - } - }; - function insidePolygon(p) { - var wn = 0, n = polygon.length, y = p[1]; - for (var i = 0; i < n; ++i) { - for (var j = 1, v = polygon[i], m = v.length, a = v[0], b; j < m; ++j) { - b = v[j]; - if (a[1] <= y) { - if (b[1] > y && d3_cross2d(a, b, p) > 0) ++wn; - } else { - if (b[1] <= y && d3_cross2d(a, b, p) < 0) --wn; - } - a = b; - } - } - return wn !== 0; - } - function interpolate(from, to, direction, listener) { - var a = 0, a1 = 0; - if (from == null || (a = corner(from, direction)) !== (a1 = corner(to, direction)) || comparePoints(from, to) < 0 ^ direction > 0) { - do { - listener.point(a === 0 || a === 3 ? x0 : x1, a > 1 ? y1 : y0); - } while ((a = (a + direction + 4) % 4) !== a1); - } else { - listener.point(to[0], to[1]); - } - } - function pointVisible(x, y) { - return x0 <= x && x <= x1 && y0 <= y && y <= y1; - } - function point(x, y) { - if (pointVisible(x, y)) listener.point(x, y); - } - var x__, y__, v__, x_, y_, v_, first, clean; - function lineStart() { - clip.point = linePoint; - if (polygon) polygon.push(ring = []); - first = true; - v_ = false; - x_ = y_ = NaN; - } - function lineEnd() { - if (segments) { - linePoint(x__, y__); - if (v__ && v_) bufferListener.rejoin(); - segments.push(bufferListener.buffer()); - } - clip.point = point; - if (v_) listener.lineEnd(); - } - function linePoint(x, y) { - x = Math.max(-d3_geo_clipExtentMAX, Math.min(d3_geo_clipExtentMAX, x)); - y = Math.max(-d3_geo_clipExtentMAX, Math.min(d3_geo_clipExtentMAX, y)); - var v = pointVisible(x, y); - if (polygon) ring.push([ x, y ]); - if (first) { - x__ = x, y__ = y, v__ = v; - first = false; - if (v) { - listener.lineStart(); - listener.point(x, y); - } - } else { - if (v && v_) listener.point(x, y); else { - var l = { - a: { - x: x_, - y: y_ - }, - b: { - x: x, - y: y - } - }; - if (clipLine(l)) { - if (!v_) { - listener.lineStart(); - listener.point(l.a.x, l.a.y); - } - listener.point(l.b.x, l.b.y); - if (!v) listener.lineEnd(); - clean = false; - } else if (v) { - listener.lineStart(); - listener.point(x, y); - clean = false; - } - } - } - x_ = x, y_ = y, v_ = v; - } - return clip; - }; - function corner(p, direction) { - return abs(p[0] - x0) < ε ? direction > 0 ? 0 : 3 : abs(p[0] - x1) < ε ? direction > 0 ? 2 : 1 : abs(p[1] - y0) < ε ? direction > 0 ? 1 : 0 : direction > 0 ? 3 : 2; - } - function compare(a, b) { - return comparePoints(a.x, b.x); - } - function comparePoints(a, b) { - var ca = corner(a, 1), cb = corner(b, 1); - return ca !== cb ? ca - cb : ca === 0 ? b[1] - a[1] : ca === 1 ? a[0] - b[0] : ca === 2 ? a[1] - b[1] : b[0] - a[0]; - } - } - function d3_geo_compose(a, b) { - function compose(x, y) { - return x = a(x, y), b(x[0], x[1]); - } - if (a.invert && b.invert) compose.invert = function(x, y) { - return x = b.invert(x, y), x && a.invert(x[0], x[1]); - }; - return compose; - } - function d3_geo_conic(projectAt) { - var φ0 = 0, φ1 = π / 3, m = d3_geo_projectionMutator(projectAt), p = m(φ0, φ1); - p.parallels = function(_) { - if (!arguments.length) return [ φ0 / π * 180, φ1 / π * 180 ]; - return m(φ0 = _[0] * π / 180, φ1 = _[1] * π / 180); - }; - return p; - } - function d3_geo_conicEqualArea(φ0, φ1) { - var sinφ0 = Math.sin(φ0), n = (sinφ0 + Math.sin(φ1)) / 2, C = 1 + sinφ0 * (2 * n - sinφ0), ρ0 = Math.sqrt(C) / n; - function forward(λ, φ) { - var ρ = Math.sqrt(C - 2 * n * Math.sin(φ)) / n; - return [ ρ * Math.sin(λ *= n), ρ0 - ρ * Math.cos(λ) ]; - } - forward.invert = function(x, y) { - var ρ0_y = ρ0 - y; - return [ Math.atan2(x, ρ0_y) / n, d3_asin((C - (x * x + ρ0_y * ρ0_y) * n * n) / (2 * n)) ]; - }; - return forward; - } - (d3.geo.conicEqualArea = function() { - return d3_geo_conic(d3_geo_conicEqualArea); - }).raw = d3_geo_conicEqualArea; - d3.geo.albers = function() { - return d3.geo.conicEqualArea().rotate([ 96, 0 ]).center([ -.6, 38.7 ]).parallels([ 29.5, 45.5 ]).scale(1070); - }; - d3.geo.albersUsa = function() { - var lower48 = d3.geo.albers(); - var alaska = d3.geo.conicEqualArea().rotate([ 154, 0 ]).center([ -2, 58.5 ]).parallels([ 55, 65 ]); - var hawaii = d3.geo.conicEqualArea().rotate([ 157, 0 ]).center([ -3, 19.9 ]).parallels([ 8, 18 ]); - var point, pointStream = { - point: function(x, y) { - point = [ x, y ]; - } - }, lower48Point, alaskaPoint, hawaiiPoint; - function albersUsa(coordinates) { - var x = coordinates[0], y = coordinates[1]; - point = null; - (lower48Point(x, y), point) || (alaskaPoint(x, y), point) || hawaiiPoint(x, y); - return point; - } - albersUsa.invert = function(coordinates) { - var k = lower48.scale(), t = lower48.translate(), x = (coordinates[0] - t[0]) / k, y = (coordinates[1] - t[1]) / k; - return (y >= .12 && y < .234 && x >= -.425 && x < -.214 ? alaska : y >= .166 && y < .234 && x >= -.214 && x < -.115 ? hawaii : lower48).invert(coordinates); - }; - albersUsa.stream = function(stream) { - var lower48Stream = lower48.stream(stream), alaskaStream = alaska.stream(stream), hawaiiStream = hawaii.stream(stream); - return { - point: function(x, y) { - lower48Stream.point(x, y); - alaskaStream.point(x, y); - hawaiiStream.point(x, y); - }, - sphere: function() { - lower48Stream.sphere(); - alaskaStream.sphere(); - hawaiiStream.sphere(); - }, - lineStart: function() { - lower48Stream.lineStart(); - alaskaStream.lineStart(); - hawaiiStream.lineStart(); - }, - lineEnd: function() { - lower48Stream.lineEnd(); - alaskaStream.lineEnd(); - hawaiiStream.lineEnd(); - }, - polygonStart: function() { - lower48Stream.polygonStart(); - alaskaStream.polygonStart(); - hawaiiStream.polygonStart(); - }, - polygonEnd: function() { - lower48Stream.polygonEnd(); - alaskaStream.polygonEnd(); - hawaiiStream.polygonEnd(); - } - }; - }; - albersUsa.precision = function(_) { - if (!arguments.length) return lower48.precision(); - lower48.precision(_); - alaska.precision(_); - hawaii.precision(_); - return albersUsa; - }; - albersUsa.scale = function(_) { - if (!arguments.length) return lower48.scale(); - lower48.scale(_); - alaska.scale(_ * .35); - hawaii.scale(_); - return albersUsa.translate(lower48.translate()); - }; - albersUsa.translate = function(_) { - if (!arguments.length) return lower48.translate(); - var k = lower48.scale(), x = +_[0], y = +_[1]; - lower48Point = lower48.translate(_).clipExtent([ [ x - .455 * k, y - .238 * k ], [ x + .455 * k, y + .238 * k ] ]).stream(pointStream).point; - alaskaPoint = alaska.translate([ x - .307 * k, y + .201 * k ]).clipExtent([ [ x - .425 * k + ε, y + .12 * k + ε ], [ x - .214 * k - ε, y + .234 * k - ε ] ]).stream(pointStream).point; - hawaiiPoint = hawaii.translate([ x - .205 * k, y + .212 * k ]).clipExtent([ [ x - .214 * k + ε, y + .166 * k + ε ], [ x - .115 * k - ε, y + .234 * k - ε ] ]).stream(pointStream).point; - return albersUsa; - }; - return albersUsa.scale(1070); - }; - var d3_geo_pathAreaSum, d3_geo_pathAreaPolygon, d3_geo_pathArea = { - point: d3_noop, - lineStart: d3_noop, - lineEnd: d3_noop, - polygonStart: function() { - d3_geo_pathAreaPolygon = 0; - d3_geo_pathArea.lineStart = d3_geo_pathAreaRingStart; - }, - polygonEnd: function() { - d3_geo_pathArea.lineStart = d3_geo_pathArea.lineEnd = d3_geo_pathArea.point = d3_noop; - d3_geo_pathAreaSum += abs(d3_geo_pathAreaPolygon / 2); - } - }; - function d3_geo_pathAreaRingStart() { - var x00, y00, x0, y0; - d3_geo_pathArea.point = function(x, y) { - d3_geo_pathArea.point = nextPoint; - x00 = x0 = x, y00 = y0 = y; - }; - function nextPoint(x, y) { - d3_geo_pathAreaPolygon += y0 * x - x0 * y; - x0 = x, y0 = y; - } - d3_geo_pathArea.lineEnd = function() { - nextPoint(x00, y00); - }; - } - var d3_geo_pathBoundsX0, d3_geo_pathBoundsY0, d3_geo_pathBoundsX1, d3_geo_pathBoundsY1; - var d3_geo_pathBounds = { - point: d3_geo_pathBoundsPoint, - lineStart: d3_noop, - lineEnd: d3_noop, - polygonStart: d3_noop, - polygonEnd: d3_noop - }; - function d3_geo_pathBoundsPoint(x, y) { - if (x < d3_geo_pathBoundsX0) d3_geo_pathBoundsX0 = x; - if (x > d3_geo_pathBoundsX1) d3_geo_pathBoundsX1 = x; - if (y < d3_geo_pathBoundsY0) d3_geo_pathBoundsY0 = y; - if (y > d3_geo_pathBoundsY1) d3_geo_pathBoundsY1 = y; - } - function d3_geo_pathBuffer() { - var pointCircle = d3_geo_pathBufferCircle(4.5), buffer = []; - var stream = { - point: point, - lineStart: function() { - stream.point = pointLineStart; - }, - lineEnd: lineEnd, - polygonStart: function() { - stream.lineEnd = lineEndPolygon; - }, - polygonEnd: function() { - stream.lineEnd = lineEnd; - stream.point = point; - }, - pointRadius: function(_) { - pointCircle = d3_geo_pathBufferCircle(_); - return stream; - }, - result: function() { - if (buffer.length) { - var result = buffer.join(""); - buffer = []; - return result; - } - } - }; - function point(x, y) { - buffer.push("M", x, ",", y, pointCircle); - } - function pointLineStart(x, y) { - buffer.push("M", x, ",", y); - stream.point = pointLine; - } - function pointLine(x, y) { - buffer.push("L", x, ",", y); - } - function lineEnd() { - stream.point = point; - } - function lineEndPolygon() { - buffer.push("Z"); - } - return stream; - } - function d3_geo_pathBufferCircle(radius) { - return "m0," + radius + "a" + radius + "," + radius + " 0 1,1 0," + -2 * radius + "a" + radius + "," + radius + " 0 1,1 0," + 2 * radius + "z"; - } - var d3_geo_pathCentroid = { - point: d3_geo_pathCentroidPoint, - lineStart: d3_geo_pathCentroidLineStart, - lineEnd: d3_geo_pathCentroidLineEnd, - polygonStart: function() { - d3_geo_pathCentroid.lineStart = d3_geo_pathCentroidRingStart; - }, - polygonEnd: function() { - d3_geo_pathCentroid.point = d3_geo_pathCentroidPoint; - d3_geo_pathCentroid.lineStart = d3_geo_pathCentroidLineStart; - d3_geo_pathCentroid.lineEnd = d3_geo_pathCentroidLineEnd; - } - }; - function d3_geo_pathCentroidPoint(x, y) { - d3_geo_centroidX0 += x; - d3_geo_centroidY0 += y; - ++d3_geo_centroidZ0; - } - function d3_geo_pathCentroidLineStart() { - var x0, y0; - d3_geo_pathCentroid.point = function(x, y) { - d3_geo_pathCentroid.point = nextPoint; - d3_geo_pathCentroidPoint(x0 = x, y0 = y); - }; - function nextPoint(x, y) { - var dx = x - x0, dy = y - y0, z = Math.sqrt(dx * dx + dy * dy); - d3_geo_centroidX1 += z * (x0 + x) / 2; - d3_geo_centroidY1 += z * (y0 + y) / 2; - d3_geo_centroidZ1 += z; - d3_geo_pathCentroidPoint(x0 = x, y0 = y); - } - } - function d3_geo_pathCentroidLineEnd() { - d3_geo_pathCentroid.point = d3_geo_pathCentroidPoint; - } - function d3_geo_pathCentroidRingStart() { - var x00, y00, x0, y0; - d3_geo_pathCentroid.point = function(x, y) { - d3_geo_pathCentroid.point = nextPoint; - d3_geo_pathCentroidPoint(x00 = x0 = x, y00 = y0 = y); - }; - function nextPoint(x, y) { - var dx = x - x0, dy = y - y0, z = Math.sqrt(dx * dx + dy * dy); - d3_geo_centroidX1 += z * (x0 + x) / 2; - d3_geo_centroidY1 += z * (y0 + y) / 2; - d3_geo_centroidZ1 += z; - z = y0 * x - x0 * y; - d3_geo_centroidX2 += z * (x0 + x); - d3_geo_centroidY2 += z * (y0 + y); - d3_geo_centroidZ2 += z * 3; - d3_geo_pathCentroidPoint(x0 = x, y0 = y); - } - d3_geo_pathCentroid.lineEnd = function() { - nextPoint(x00, y00); - }; - } - function d3_geo_pathContext(context) { - var pointRadius = 4.5; - var stream = { - point: point, - lineStart: function() { - stream.point = pointLineStart; - }, - lineEnd: lineEnd, - polygonStart: function() { - stream.lineEnd = lineEndPolygon; - }, - polygonEnd: function() { - stream.lineEnd = lineEnd; - stream.point = point; - }, - pointRadius: function(_) { - pointRadius = _; - return stream; - }, - result: d3_noop - }; - function point(x, y) { - context.moveTo(x, y); - context.arc(x, y, pointRadius, 0, τ); - } - function pointLineStart(x, y) { - context.moveTo(x, y); - stream.point = pointLine; - } - function pointLine(x, y) { - context.lineTo(x, y); - } - function lineEnd() { - stream.point = point; - } - function lineEndPolygon() { - context.closePath(); - } - return stream; - } - function d3_geo_resample(project) { - var δ2 = .5, cosMinDistance = Math.cos(30 * d3_radians), maxDepth = 16; - function resample(stream) { - return (maxDepth ? resampleRecursive : resampleNone)(stream); - } - function resampleNone(stream) { - return d3_geo_transformPoint(stream, function(x, y) { - x = project(x, y); - stream.point(x[0], x[1]); - }); - } - function resampleRecursive(stream) { - var λ00, φ00, x00, y00, a00, b00, c00, λ0, x0, y0, a0, b0, c0; - var resample = { - point: point, - lineStart: lineStart, - lineEnd: lineEnd, - polygonStart: function() { - stream.polygonStart(); - resample.lineStart = ringStart; - }, - polygonEnd: function() { - stream.polygonEnd(); - resample.lineStart = lineStart; - } - }; - function point(x, y) { - x = project(x, y); - stream.point(x[0], x[1]); - } - function lineStart() { - x0 = NaN; - resample.point = linePoint; - stream.lineStart(); - } - function linePoint(λ, φ) { - var c = d3_geo_cartesian([ λ, φ ]), p = project(λ, φ); - resampleLineTo(x0, y0, λ0, a0, b0, c0, x0 = p[0], y0 = p[1], λ0 = λ, a0 = c[0], b0 = c[1], c0 = c[2], maxDepth, stream); - stream.point(x0, y0); - } - function lineEnd() { - resample.point = point; - stream.lineEnd(); - } - function ringStart() { - lineStart(); - resample.point = ringPoint; - resample.lineEnd = ringEnd; - } - function ringPoint(λ, φ) { - linePoint(λ00 = λ, φ00 = φ), x00 = x0, y00 = y0, a00 = a0, b00 = b0, c00 = c0; - resample.point = linePoint; - } - function ringEnd() { - resampleLineTo(x0, y0, λ0, a0, b0, c0, x00, y00, λ00, a00, b00, c00, maxDepth, stream); - resample.lineEnd = lineEnd; - lineEnd(); - } - return resample; - } - function resampleLineTo(x0, y0, λ0, a0, b0, c0, x1, y1, λ1, a1, b1, c1, depth, stream) { - var dx = x1 - x0, dy = y1 - y0, d2 = dx * dx + dy * dy; - if (d2 > 4 * δ2 && depth--) { - var a = a0 + a1, b = b0 + b1, c = c0 + c1, m = Math.sqrt(a * a + b * b + c * c), φ2 = Math.asin(c /= m), λ2 = abs(abs(c) - 1) < ε || abs(λ0 - λ1) < ε ? (λ0 + λ1) / 2 : Math.atan2(b, a), p = project(λ2, φ2), x2 = p[0], y2 = p[1], dx2 = x2 - x0, dy2 = y2 - y0, dz = dy * dx2 - dx * dy2; - if (dz * dz / d2 > δ2 || abs((dx * dx2 + dy * dy2) / d2 - .5) > .3 || a0 * a1 + b0 * b1 + c0 * c1 < cosMinDistance) { - resampleLineTo(x0, y0, λ0, a0, b0, c0, x2, y2, λ2, a /= m, b /= m, c, depth, stream); - stream.point(x2, y2); - resampleLineTo(x2, y2, λ2, a, b, c, x1, y1, λ1, a1, b1, c1, depth, stream); - } - } - } - resample.precision = function(_) { - if (!arguments.length) return Math.sqrt(δ2); - maxDepth = (δ2 = _ * _) > 0 && 16; - return resample; - }; - return resample; - } - d3.geo.path = function() { - var pointRadius = 4.5, projection, context, projectStream, contextStream, cacheStream; - function path(object) { - if (object) { - if (typeof pointRadius === "function") contextStream.pointRadius(+pointRadius.apply(this, arguments)); - if (!cacheStream || !cacheStream.valid) cacheStream = projectStream(contextStream); - d3.geo.stream(object, cacheStream); - } - return contextStream.result(); - } - path.area = function(object) { - d3_geo_pathAreaSum = 0; - d3.geo.stream(object, projectStream(d3_geo_pathArea)); - return d3_geo_pathAreaSum; - }; - path.centroid = function(object) { - d3_geo_centroidX0 = d3_geo_centroidY0 = d3_geo_centroidZ0 = d3_geo_centroidX1 = d3_geo_centroidY1 = d3_geo_centroidZ1 = d3_geo_centroidX2 = d3_geo_centroidY2 = d3_geo_centroidZ2 = 0; - d3.geo.stream(object, projectStream(d3_geo_pathCentroid)); - return d3_geo_centroidZ2 ? [ d3_geo_centroidX2 / d3_geo_centroidZ2, d3_geo_centroidY2 / d3_geo_centroidZ2 ] : d3_geo_centroidZ1 ? [ d3_geo_centroidX1 / d3_geo_centroidZ1, d3_geo_centroidY1 / d3_geo_centroidZ1 ] : d3_geo_centroidZ0 ? [ d3_geo_centroidX0 / d3_geo_centroidZ0, d3_geo_centroidY0 / d3_geo_centroidZ0 ] : [ NaN, NaN ]; - }; - path.bounds = function(object) { - d3_geo_pathBoundsX1 = d3_geo_pathBoundsY1 = -(d3_geo_pathBoundsX0 = d3_geo_pathBoundsY0 = Infinity); - d3.geo.stream(object, projectStream(d3_geo_pathBounds)); - return [ [ d3_geo_pathBoundsX0, d3_geo_pathBoundsY0 ], [ d3_geo_pathBoundsX1, d3_geo_pathBoundsY1 ] ]; - }; - path.projection = function(_) { - if (!arguments.length) return projection; - projectStream = (projection = _) ? _.stream || d3_geo_pathProjectStream(_) : d3_identity; - return reset(); - }; - path.context = function(_) { - if (!arguments.length) return context; - contextStream = (context = _) == null ? new d3_geo_pathBuffer() : new d3_geo_pathContext(_); - if (typeof pointRadius !== "function") contextStream.pointRadius(pointRadius); - return reset(); - }; - path.pointRadius = function(_) { - if (!arguments.length) return pointRadius; - pointRadius = typeof _ === "function" ? _ : (contextStream.pointRadius(+_), +_); - return path; - }; - function reset() { - cacheStream = null; - return path; - } - return path.projection(d3.geo.albersUsa()).context(null); - }; - function d3_geo_pathProjectStream(project) { - var resample = d3_geo_resample(function(x, y) { - return project([ x * d3_degrees, y * d3_degrees ]); - }); - return function(stream) { - return d3_geo_projectionRadians(resample(stream)); - }; - } - d3.geo.transform = function(methods) { - return { - stream: function(stream) { - var transform = new d3_geo_transform(stream); - for (var k in methods) transform[k] = methods[k]; - return transform; - } - }; - }; - function d3_geo_transform(stream) { - this.stream = stream; - } - d3_geo_transform.prototype = { - point: function(x, y) { - this.stream.point(x, y); - }, - sphere: function() { - this.stream.sphere(); - }, - lineStart: function() { - this.stream.lineStart(); - }, - lineEnd: function() { - this.stream.lineEnd(); - }, - polygonStart: function() { - this.stream.polygonStart(); - }, - polygonEnd: function() { - this.stream.polygonEnd(); - } - }; - function d3_geo_transformPoint(stream, point) { - return { - point: point, - sphere: function() { - stream.sphere(); - }, - lineStart: function() { - stream.lineStart(); - }, - lineEnd: function() { - stream.lineEnd(); - }, - polygonStart: function() { - stream.polygonStart(); - }, - polygonEnd: function() { - stream.polygonEnd(); - } - }; - } - d3.geo.projection = d3_geo_projection; - d3.geo.projectionMutator = d3_geo_projectionMutator; - function d3_geo_projection(project) { - return d3_geo_projectionMutator(function() { - return project; - })(); - } - function d3_geo_projectionMutator(projectAt) { - var project, rotate, projectRotate, projectResample = d3_geo_resample(function(x, y) { - x = project(x, y); - return [ x[0] * k + δx, δy - x[1] * k ]; - }), k = 150, x = 480, y = 250, λ = 0, φ = 0, δλ = 0, δφ = 0, δγ = 0, δx, δy, preclip = d3_geo_clipAntimeridian, postclip = d3_identity, clipAngle = null, clipExtent = null, stream; - function projection(point) { - point = projectRotate(point[0] * d3_radians, point[1] * d3_radians); - return [ point[0] * k + δx, δy - point[1] * k ]; - } - function invert(point) { - point = projectRotate.invert((point[0] - δx) / k, (δy - point[1]) / k); - return point && [ point[0] * d3_degrees, point[1] * d3_degrees ]; - } - projection.stream = function(output) { - if (stream) stream.valid = false; - stream = d3_geo_projectionRadians(preclip(rotate, projectResample(postclip(output)))); - stream.valid = true; - return stream; - }; - projection.clipAngle = function(_) { - if (!arguments.length) return clipAngle; - preclip = _ == null ? (clipAngle = _, d3_geo_clipAntimeridian) : d3_geo_clipCircle((clipAngle = +_) * d3_radians); - return invalidate(); - }; - projection.clipExtent = function(_) { - if (!arguments.length) return clipExtent; - clipExtent = _; - postclip = _ ? d3_geo_clipExtent(_[0][0], _[0][1], _[1][0], _[1][1]) : d3_identity; - return invalidate(); - }; - projection.scale = function(_) { - if (!arguments.length) return k; - k = +_; - return reset(); - }; - projection.translate = function(_) { - if (!arguments.length) return [ x, y ]; - x = +_[0]; - y = +_[1]; - return reset(); - }; - projection.center = function(_) { - if (!arguments.length) return [ λ * d3_degrees, φ * d3_degrees ]; - λ = _[0] % 360 * d3_radians; - φ = _[1] % 360 * d3_radians; - return reset(); - }; - projection.rotate = function(_) { - if (!arguments.length) return [ δλ * d3_degrees, δφ * d3_degrees, δγ * d3_degrees ]; - δλ = _[0] % 360 * d3_radians; - δφ = _[1] % 360 * d3_radians; - δγ = _.length > 2 ? _[2] % 360 * d3_radians : 0; - return reset(); - }; - d3.rebind(projection, projectResample, "precision"); - function reset() { - projectRotate = d3_geo_compose(rotate = d3_geo_rotation(δλ, δφ, δγ), project); - var center = project(λ, φ); - δx = x - center[0] * k; - δy = y + center[1] * k; - return invalidate(); - } - function invalidate() { - if (stream) stream.valid = false, stream = null; - return projection; - } - return function() { - project = projectAt.apply(this, arguments); - projection.invert = project.invert && invert; - return reset(); - }; - } - function d3_geo_projectionRadians(stream) { - return d3_geo_transformPoint(stream, function(x, y) { - stream.point(x * d3_radians, y * d3_radians); - }); - } - function d3_geo_equirectangular(λ, φ) { - return [ λ, φ ]; - } - (d3.geo.equirectangular = function() { - return d3_geo_projection(d3_geo_equirectangular); - }).raw = d3_geo_equirectangular.invert = d3_geo_equirectangular; - d3.geo.rotation = function(rotate) { - rotate = d3_geo_rotation(rotate[0] % 360 * d3_radians, rotate[1] * d3_radians, rotate.length > 2 ? rotate[2] * d3_radians : 0); - function forward(coordinates) { - coordinates = rotate(coordinates[0] * d3_radians, coordinates[1] * d3_radians); - return coordinates[0] *= d3_degrees, coordinates[1] *= d3_degrees, coordinates; - } - forward.invert = function(coordinates) { - coordinates = rotate.invert(coordinates[0] * d3_radians, coordinates[1] * d3_radians); - return coordinates[0] *= d3_degrees, coordinates[1] *= d3_degrees, coordinates; - }; - return forward; - }; - function d3_geo_identityRotation(λ, φ) { - return [ λ > π ? λ - τ : λ < -π ? λ + τ : λ, φ ]; - } - d3_geo_identityRotation.invert = d3_geo_equirectangular; - function d3_geo_rotation(δλ, δφ, δγ) { - return δλ ? δφ || δγ ? d3_geo_compose(d3_geo_rotationλ(δλ), d3_geo_rotationφγ(δφ, δγ)) : d3_geo_rotationλ(δλ) : δφ || δγ ? d3_geo_rotationφγ(δφ, δγ) : d3_geo_identityRotation; - } - function d3_geo_forwardRotationλ(δλ) { - return function(λ, φ) { - return λ += δλ, [ λ > π ? λ - τ : λ < -π ? λ + τ : λ, φ ]; - }; - } - function d3_geo_rotationλ(δλ) { - var rotation = d3_geo_forwardRotationλ(δλ); - rotation.invert = d3_geo_forwardRotationλ(-δλ); - return rotation; - } - function d3_geo_rotationφγ(δφ, δγ) { - var cosδφ = Math.cos(δφ), sinδφ = Math.sin(δφ), cosδγ = Math.cos(δγ), sinδγ = Math.sin(δγ); - function rotation(λ, φ) { - var cosφ = Math.cos(φ), x = Math.cos(λ) * cosφ, y = Math.sin(λ) * cosφ, z = Math.sin(φ), k = z * cosδφ + x * sinδφ; - return [ Math.atan2(y * cosδγ - k * sinδγ, x * cosδφ - z * sinδφ), d3_asin(k * cosδγ + y * sinδγ) ]; - } - rotation.invert = function(λ, φ) { - var cosφ = Math.cos(φ), x = Math.cos(λ) * cosφ, y = Math.sin(λ) * cosφ, z = Math.sin(φ), k = z * cosδγ - y * sinδγ; - return [ Math.atan2(y * cosδγ + z * sinδγ, x * cosδφ + k * sinδφ), d3_asin(k * cosδφ - x * sinδφ) ]; - }; - return rotation; - } - d3.geo.circle = function() { - var origin = [ 0, 0 ], angle, precision = 6, interpolate; - function circle() { - var center = typeof origin === "function" ? origin.apply(this, arguments) : origin, rotate = d3_geo_rotation(-center[0] * d3_radians, -center[1] * d3_radians, 0).invert, ring = []; - interpolate(null, null, 1, { - point: function(x, y) { - ring.push(x = rotate(x, y)); - x[0] *= d3_degrees, x[1] *= d3_degrees; - } - }); - return { - type: "Polygon", - coordinates: [ ring ] - }; - } - circle.origin = function(x) { - if (!arguments.length) return origin; - origin = x; - return circle; - }; - circle.angle = function(x) { - if (!arguments.length) return angle; - interpolate = d3_geo_circleInterpolate((angle = +x) * d3_radians, precision * d3_radians); - return circle; - }; - circle.precision = function(_) { - if (!arguments.length) return precision; - interpolate = d3_geo_circleInterpolate(angle * d3_radians, (precision = +_) * d3_radians); - return circle; - }; - return circle.angle(90); - }; - function d3_geo_circleInterpolate(radius, precision) { - var cr = Math.cos(radius), sr = Math.sin(radius); - return function(from, to, direction, listener) { - var step = direction * precision; - if (from != null) { - from = d3_geo_circleAngle(cr, from); - to = d3_geo_circleAngle(cr, to); - if (direction > 0 ? from < to : from > to) from += direction * τ; - } else { - from = radius + direction * τ; - to = radius - .5 * step; - } - for (var point, t = from; direction > 0 ? t > to : t < to; t -= step) { - listener.point((point = d3_geo_spherical([ cr, -sr * Math.cos(t), -sr * Math.sin(t) ]))[0], point[1]); - } - }; - } - function d3_geo_circleAngle(cr, point) { - var a = d3_geo_cartesian(point); - a[0] -= cr; - d3_geo_cartesianNormalize(a); - var angle = d3_acos(-a[1]); - return ((-a[2] < 0 ? -angle : angle) + 2 * Math.PI - ε) % (2 * Math.PI); - } - d3.geo.distance = function(a, b) { - var Δλ = (b[0] - a[0]) * d3_radians, φ0 = a[1] * d3_radians, φ1 = b[1] * d3_radians, sinΔλ = Math.sin(Δλ), cosΔλ = Math.cos(Δλ), sinφ0 = Math.sin(φ0), cosφ0 = Math.cos(φ0), sinφ1 = Math.sin(φ1), cosφ1 = Math.cos(φ1), t; - return Math.atan2(Math.sqrt((t = cosφ1 * sinΔλ) * t + (t = cosφ0 * sinφ1 - sinφ0 * cosφ1 * cosΔλ) * t), sinφ0 * sinφ1 + cosφ0 * cosφ1 * cosΔλ); - }; - d3.geo.graticule = function() { - var x1, x0, X1, X0, y1, y0, Y1, Y0, dx = 10, dy = dx, DX = 90, DY = 360, x, y, X, Y, precision = 2.5; - function graticule() { - return { - type: "MultiLineString", - coordinates: lines() - }; - } - function lines() { - return d3.range(Math.ceil(X0 / DX) * DX, X1, DX).map(X).concat(d3.range(Math.ceil(Y0 / DY) * DY, Y1, DY).map(Y)).concat(d3.range(Math.ceil(x0 / dx) * dx, x1, dx).filter(function(x) { - return abs(x % DX) > ε; - }).map(x)).concat(d3.range(Math.ceil(y0 / dy) * dy, y1, dy).filter(function(y) { - return abs(y % DY) > ε; - }).map(y)); - } - graticule.lines = function() { - return lines().map(function(coordinates) { - return { - type: "LineString", - coordinates: coordinates - }; - }); - }; - graticule.outline = function() { - return { - type: "Polygon", - coordinates: [ X(X0).concat(Y(Y1).slice(1), X(X1).reverse().slice(1), Y(Y0).reverse().slice(1)) ] - }; - }; - graticule.extent = function(_) { - if (!arguments.length) return graticule.minorExtent(); - return graticule.majorExtent(_).minorExtent(_); - }; - graticule.majorExtent = function(_) { - if (!arguments.length) return [ [ X0, Y0 ], [ X1, Y1 ] ]; - X0 = +_[0][0], X1 = +_[1][0]; - Y0 = +_[0][1], Y1 = +_[1][1]; - if (X0 > X1) _ = X0, X0 = X1, X1 = _; - if (Y0 > Y1) _ = Y0, Y0 = Y1, Y1 = _; - return graticule.precision(precision); - }; - graticule.minorExtent = function(_) { - if (!arguments.length) return [ [ x0, y0 ], [ x1, y1 ] ]; - x0 = +_[0][0], x1 = +_[1][0]; - y0 = +_[0][1], y1 = +_[1][1]; - if (x0 > x1) _ = x0, x0 = x1, x1 = _; - if (y0 > y1) _ = y0, y0 = y1, y1 = _; - return graticule.precision(precision); - }; - graticule.step = function(_) { - if (!arguments.length) return graticule.minorStep(); - return graticule.majorStep(_).minorStep(_); - }; - graticule.majorStep = function(_) { - if (!arguments.length) return [ DX, DY ]; - DX = +_[0], DY = +_[1]; - return graticule; - }; - graticule.minorStep = function(_) { - if (!arguments.length) return [ dx, dy ]; - dx = +_[0], dy = +_[1]; - return graticule; - }; - graticule.precision = function(_) { - if (!arguments.length) return precision; - precision = +_; - x = d3_geo_graticuleX(y0, y1, 90); - y = d3_geo_graticuleY(x0, x1, precision); - X = d3_geo_graticuleX(Y0, Y1, 90); - Y = d3_geo_graticuleY(X0, X1, precision); - return graticule; - }; - return graticule.majorExtent([ [ -180, -90 + ε ], [ 180, 90 - ε ] ]).minorExtent([ [ -180, -80 - ε ], [ 180, 80 + ε ] ]); - }; - function d3_geo_graticuleX(y0, y1, dy) { - var y = d3.range(y0, y1 - ε, dy).concat(y1); - return function(x) { - return y.map(function(y) { - return [ x, y ]; - }); - }; - } - function d3_geo_graticuleY(x0, x1, dx) { - var x = d3.range(x0, x1 - ε, dx).concat(x1); - return function(y) { - return x.map(function(x) { - return [ x, y ]; - }); - }; - } - function d3_source(d) { - return d.source; - } - function d3_target(d) { - return d.target; - } - d3.geo.greatArc = function() { - var source = d3_source, source_, target = d3_target, target_; - function greatArc() { - return { - type: "LineString", - coordinates: [ source_ || source.apply(this, arguments), target_ || target.apply(this, arguments) ] - }; - } - greatArc.distance = function() { - return d3.geo.distance(source_ || source.apply(this, arguments), target_ || target.apply(this, arguments)); - }; - greatArc.source = function(_) { - if (!arguments.length) return source; - source = _, source_ = typeof _ === "function" ? null : _; - return greatArc; - }; - greatArc.target = function(_) { - if (!arguments.length) return target; - target = _, target_ = typeof _ === "function" ? null : _; - return greatArc; - }; - greatArc.precision = function() { - return arguments.length ? greatArc : 0; - }; - return greatArc; - }; - d3.geo.interpolate = function(source, target) { - return d3_geo_interpolate(source[0] * d3_radians, source[1] * d3_radians, target[0] * d3_radians, target[1] * d3_radians); - }; - function d3_geo_interpolate(x0, y0, x1, y1) { - var cy0 = Math.cos(y0), sy0 = Math.sin(y0), cy1 = Math.cos(y1), sy1 = Math.sin(y1), kx0 = cy0 * Math.cos(x0), ky0 = cy0 * Math.sin(x0), kx1 = cy1 * Math.cos(x1), ky1 = cy1 * Math.sin(x1), d = 2 * Math.asin(Math.sqrt(d3_haversin(y1 - y0) + cy0 * cy1 * d3_haversin(x1 - x0))), k = 1 / Math.sin(d); - var interpolate = d ? function(t) { - var B = Math.sin(t *= d) * k, A = Math.sin(d - t) * k, x = A * kx0 + B * kx1, y = A * ky0 + B * ky1, z = A * sy0 + B * sy1; - return [ Math.atan2(y, x) * d3_degrees, Math.atan2(z, Math.sqrt(x * x + y * y)) * d3_degrees ]; - } : function() { - return [ x0 * d3_degrees, y0 * d3_degrees ]; - }; - interpolate.distance = d; - return interpolate; - } - d3.geo.length = function(object) { - d3_geo_lengthSum = 0; - d3.geo.stream(object, d3_geo_length); - return d3_geo_lengthSum; - }; - var d3_geo_lengthSum; - var d3_geo_length = { - sphere: d3_noop, - point: d3_noop, - lineStart: d3_geo_lengthLineStart, - lineEnd: d3_noop, - polygonStart: d3_noop, - polygonEnd: d3_noop - }; - function d3_geo_lengthLineStart() { - var λ0, sinφ0, cosφ0; - d3_geo_length.point = function(λ, φ) { - λ0 = λ * d3_radians, sinφ0 = Math.sin(φ *= d3_radians), cosφ0 = Math.cos(φ); - d3_geo_length.point = nextPoint; - }; - d3_geo_length.lineEnd = function() { - d3_geo_length.point = d3_geo_length.lineEnd = d3_noop; - }; - function nextPoint(λ, φ) { - var sinφ = Math.sin(φ *= d3_radians), cosφ = Math.cos(φ), t = abs((λ *= d3_radians) - λ0), cosΔλ = Math.cos(t); - d3_geo_lengthSum += Math.atan2(Math.sqrt((t = cosφ * Math.sin(t)) * t + (t = cosφ0 * sinφ - sinφ0 * cosφ * cosΔλ) * t), sinφ0 * sinφ + cosφ0 * cosφ * cosΔλ); - λ0 = λ, sinφ0 = sinφ, cosφ0 = cosφ; - } - } - function d3_geo_azimuthal(scale, angle) { - function azimuthal(λ, φ) { - var cosλ = Math.cos(λ), cosφ = Math.cos(φ), k = scale(cosλ * cosφ); - return [ k * cosφ * Math.sin(λ), k * Math.sin(φ) ]; - } - azimuthal.invert = function(x, y) { - var ρ = Math.sqrt(x * x + y * y), c = angle(ρ), sinc = Math.sin(c), cosc = Math.cos(c); - return [ Math.atan2(x * sinc, ρ * cosc), Math.asin(ρ && y * sinc / ρ) ]; - }; - return azimuthal; - } - var d3_geo_azimuthalEqualArea = d3_geo_azimuthal(function(cosλcosφ) { - return Math.sqrt(2 / (1 + cosλcosφ)); - }, function(ρ) { - return 2 * Math.asin(ρ / 2); - }); - (d3.geo.azimuthalEqualArea = function() { - return d3_geo_projection(d3_geo_azimuthalEqualArea); - }).raw = d3_geo_azimuthalEqualArea; - var d3_geo_azimuthalEquidistant = d3_geo_azimuthal(function(cosλcosφ) { - var c = Math.acos(cosλcosφ); - return c && c / Math.sin(c); - }, d3_identity); - (d3.geo.azimuthalEquidistant = function() { - return d3_geo_projection(d3_geo_azimuthalEquidistant); - }).raw = d3_geo_azimuthalEquidistant; - function d3_geo_conicConformal(φ0, φ1) { - var cosφ0 = Math.cos(φ0), t = function(φ) { - return Math.tan(π / 4 + φ / 2); - }, n = φ0 === φ1 ? Math.sin(φ0) : Math.log(cosφ0 / Math.cos(φ1)) / Math.log(t(φ1) / t(φ0)), F = cosφ0 * Math.pow(t(φ0), n) / n; - if (!n) return d3_geo_mercator; - function forward(λ, φ) { - if (F > 0) { - if (φ < -halfπ + ε) φ = -halfπ + ε; - } else { - if (φ > halfπ - ε) φ = halfπ - ε; - } - var ρ = F / Math.pow(t(φ), n); - return [ ρ * Math.sin(n * λ), F - ρ * Math.cos(n * λ) ]; - } - forward.invert = function(x, y) { - var ρ0_y = F - y, ρ = d3_sgn(n) * Math.sqrt(x * x + ρ0_y * ρ0_y); - return [ Math.atan2(x, ρ0_y) / n, 2 * Math.atan(Math.pow(F / ρ, 1 / n)) - halfπ ]; - }; - return forward; - } - (d3.geo.conicConformal = function() { - return d3_geo_conic(d3_geo_conicConformal); - }).raw = d3_geo_conicConformal; - function d3_geo_conicEquidistant(φ0, φ1) { - var cosφ0 = Math.cos(φ0), n = φ0 === φ1 ? Math.sin(φ0) : (cosφ0 - Math.cos(φ1)) / (φ1 - φ0), G = cosφ0 / n + φ0; - if (abs(n) < ε) return d3_geo_equirectangular; - function forward(λ, φ) { - var ρ = G - φ; - return [ ρ * Math.sin(n * λ), G - ρ * Math.cos(n * λ) ]; - } - forward.invert = function(x, y) { - var ρ0_y = G - y; - return [ Math.atan2(x, ρ0_y) / n, G - d3_sgn(n) * Math.sqrt(x * x + ρ0_y * ρ0_y) ]; - }; - return forward; - } - (d3.geo.conicEquidistant = function() { - return d3_geo_conic(d3_geo_conicEquidistant); - }).raw = d3_geo_conicEquidistant; - var d3_geo_gnomonic = d3_geo_azimuthal(function(cosλcosφ) { - return 1 / cosλcosφ; - }, Math.atan); - (d3.geo.gnomonic = function() { - return d3_geo_projection(d3_geo_gnomonic); - }).raw = d3_geo_gnomonic; - function d3_geo_mercator(λ, φ) { - return [ λ, Math.log(Math.tan(π / 4 + φ / 2)) ]; - } - d3_geo_mercator.invert = function(x, y) { - return [ x, 2 * Math.atan(Math.exp(y)) - halfπ ]; - }; - function d3_geo_mercatorProjection(project) { - var m = d3_geo_projection(project), scale = m.scale, translate = m.translate, clipExtent = m.clipExtent, clipAuto; - m.scale = function() { - var v = scale.apply(m, arguments); - return v === m ? clipAuto ? m.clipExtent(null) : m : v; - }; - m.translate = function() { - var v = translate.apply(m, arguments); - return v === m ? clipAuto ? m.clipExtent(null) : m : v; - }; - m.clipExtent = function(_) { - var v = clipExtent.apply(m, arguments); - if (v === m) { - if (clipAuto = _ == null) { - var k = π * scale(), t = translate(); - clipExtent([ [ t[0] - k, t[1] - k ], [ t[0] + k, t[1] + k ] ]); - } - } else if (clipAuto) { - v = null; - } - return v; - }; - return m.clipExtent(null); - } - (d3.geo.mercator = function() { - return d3_geo_mercatorProjection(d3_geo_mercator); - }).raw = d3_geo_mercator; - var d3_geo_orthographic = d3_geo_azimuthal(function() { - return 1; - }, Math.asin); - (d3.geo.orthographic = function() { - return d3_geo_projection(d3_geo_orthographic); - }).raw = d3_geo_orthographic; - var d3_geo_stereographic = d3_geo_azimuthal(function(cosλcosφ) { - return 1 / (1 + cosλcosφ); - }, function(ρ) { - return 2 * Math.atan(ρ); - }); - (d3.geo.stereographic = function() { - return d3_geo_projection(d3_geo_stereographic); - }).raw = d3_geo_stereographic; - function d3_geo_transverseMercator(λ, φ) { - return [ Math.log(Math.tan(π / 4 + φ / 2)), -λ ]; - } - d3_geo_transverseMercator.invert = function(x, y) { - return [ -y, 2 * Math.atan(Math.exp(x)) - halfπ ]; - }; - (d3.geo.transverseMercator = function() { - var projection = d3_geo_mercatorProjection(d3_geo_transverseMercator), center = projection.center, rotate = projection.rotate; - projection.center = function(_) { - return _ ? center([ -_[1], _[0] ]) : (_ = center(), [ -_[1], _[0] ]); - }; - projection.rotate = function(_) { - return _ ? rotate([ _[0], _[1], _.length > 2 ? _[2] + 90 : 90 ]) : (_ = rotate(), - [ _[0], _[1], _[2] - 90 ]); - }; - return projection.rotate([ 0, 0 ]); - }).raw = d3_geo_transverseMercator; - d3.geom = {}; - function d3_geom_pointX(d) { - return d[0]; - } - function d3_geom_pointY(d) { - return d[1]; - } - d3.geom.hull = function(vertices) { - var x = d3_geom_pointX, y = d3_geom_pointY; - if (arguments.length) return hull(vertices); - function hull(data) { - if (data.length < 3) return []; - var fx = d3_functor(x), fy = d3_functor(y), i, n = data.length, points = [], flippedPoints = []; - for (i = 0; i < n; i++) { - points.push([ +fx.call(this, data[i], i), +fy.call(this, data[i], i), i ]); - } - points.sort(d3_geom_hullOrder); - for (i = 0; i < n; i++) flippedPoints.push([ points[i][0], -points[i][1] ]); - var upper = d3_geom_hullUpper(points), lower = d3_geom_hullUpper(flippedPoints); - var skipLeft = lower[0] === upper[0], skipRight = lower[lower.length - 1] === upper[upper.length - 1], polygon = []; - for (i = upper.length - 1; i >= 0; --i) polygon.push(data[points[upper[i]][2]]); - for (i = +skipLeft; i < lower.length - skipRight; ++i) polygon.push(data[points[lower[i]][2]]); - return polygon; - } - hull.x = function(_) { - return arguments.length ? (x = _, hull) : x; - }; - hull.y = function(_) { - return arguments.length ? (y = _, hull) : y; - }; - return hull; - }; - function d3_geom_hullUpper(points) { - var n = points.length, hull = [ 0, 1 ], hs = 2; - for (var i = 2; i < n; i++) { - while (hs > 1 && d3_cross2d(points[hull[hs - 2]], points[hull[hs - 1]], points[i]) <= 0) --hs; - hull[hs++] = i; - } - return hull.slice(0, hs); - } - function d3_geom_hullOrder(a, b) { - return a[0] - b[0] || a[1] - b[1]; - } - d3.geom.polygon = function(coordinates) { - d3_subclass(coordinates, d3_geom_polygonPrototype); - return coordinates; - }; - var d3_geom_polygonPrototype = d3.geom.polygon.prototype = []; - d3_geom_polygonPrototype.area = function() { - var i = -1, n = this.length, a, b = this[n - 1], area = 0; - while (++i < n) { - a = b; - b = this[i]; - area += a[1] * b[0] - a[0] * b[1]; - } - return area * .5; - }; - d3_geom_polygonPrototype.centroid = function(k) { - var i = -1, n = this.length, x = 0, y = 0, a, b = this[n - 1], c; - if (!arguments.length) k = -1 / (6 * this.area()); - while (++i < n) { - a = b; - b = this[i]; - c = a[0] * b[1] - b[0] * a[1]; - x += (a[0] + b[0]) * c; - y += (a[1] + b[1]) * c; - } - return [ x * k, y * k ]; - }; - d3_geom_polygonPrototype.clip = function(subject) { - var input, closed = d3_geom_polygonClosed(subject), i = -1, n = this.length - d3_geom_polygonClosed(this), j, m, a = this[n - 1], b, c, d; - while (++i < n) { - input = subject.slice(); - subject.length = 0; - b = this[i]; - c = input[(m = input.length - closed) - 1]; - j = -1; - while (++j < m) { - d = input[j]; - if (d3_geom_polygonInside(d, a, b)) { - if (!d3_geom_polygonInside(c, a, b)) { - subject.push(d3_geom_polygonIntersect(c, d, a, b)); - } - subject.push(d); - } else if (d3_geom_polygonInside(c, a, b)) { - subject.push(d3_geom_polygonIntersect(c, d, a, b)); - } - c = d; - } - if (closed) subject.push(subject[0]); - a = b; - } - return subject; - }; - function d3_geom_polygonInside(p, a, b) { - return (b[0] - a[0]) * (p[1] - a[1]) < (b[1] - a[1]) * (p[0] - a[0]); - } - function d3_geom_polygonIntersect(c, d, a, b) { - var x1 = c[0], x3 = a[0], x21 = d[0] - x1, x43 = b[0] - x3, y1 = c[1], y3 = a[1], y21 = d[1] - y1, y43 = b[1] - y3, ua = (x43 * (y1 - y3) - y43 * (x1 - x3)) / (y43 * x21 - x43 * y21); - return [ x1 + ua * x21, y1 + ua * y21 ]; - } - function d3_geom_polygonClosed(coordinates) { - var a = coordinates[0], b = coordinates[coordinates.length - 1]; - return !(a[0] - b[0] || a[1] - b[1]); - } - var d3_geom_voronoiEdges, d3_geom_voronoiCells, d3_geom_voronoiBeaches, d3_geom_voronoiBeachPool = [], d3_geom_voronoiFirstCircle, d3_geom_voronoiCircles, d3_geom_voronoiCirclePool = []; - function d3_geom_voronoiBeach() { - d3_geom_voronoiRedBlackNode(this); - this.edge = this.site = this.circle = null; - } - function d3_geom_voronoiCreateBeach(site) { - var beach = d3_geom_voronoiBeachPool.pop() || new d3_geom_voronoiBeach(); - beach.site = site; - return beach; - } - function d3_geom_voronoiDetachBeach(beach) { - d3_geom_voronoiDetachCircle(beach); - d3_geom_voronoiBeaches.remove(beach); - d3_geom_voronoiBeachPool.push(beach); - d3_geom_voronoiRedBlackNode(beach); - } - function d3_geom_voronoiRemoveBeach(beach) { - var circle = beach.circle, x = circle.x, y = circle.cy, vertex = { - x: x, - y: y - }, previous = beach.P, next = beach.N, disappearing = [ beach ]; - d3_geom_voronoiDetachBeach(beach); - var lArc = previous; - while (lArc.circle && abs(x - lArc.circle.x) < ε && abs(y - lArc.circle.cy) < ε) { - previous = lArc.P; - disappearing.unshift(lArc); - d3_geom_voronoiDetachBeach(lArc); - lArc = previous; - } - disappearing.unshift(lArc); - d3_geom_voronoiDetachCircle(lArc); - var rArc = next; - while (rArc.circle && abs(x - rArc.circle.x) < ε && abs(y - rArc.circle.cy) < ε) { - next = rArc.N; - disappearing.push(rArc); - d3_geom_voronoiDetachBeach(rArc); - rArc = next; - } - disappearing.push(rArc); - d3_geom_voronoiDetachCircle(rArc); - var nArcs = disappearing.length, iArc; - for (iArc = 1; iArc < nArcs; ++iArc) { - rArc = disappearing[iArc]; - lArc = disappearing[iArc - 1]; - d3_geom_voronoiSetEdgeEnd(rArc.edge, lArc.site, rArc.site, vertex); - } - lArc = disappearing[0]; - rArc = disappearing[nArcs - 1]; - rArc.edge = d3_geom_voronoiCreateEdge(lArc.site, rArc.site, null, vertex); - d3_geom_voronoiAttachCircle(lArc); - d3_geom_voronoiAttachCircle(rArc); - } - function d3_geom_voronoiAddBeach(site) { - var x = site.x, directrix = site.y, lArc, rArc, dxl, dxr, node = d3_geom_voronoiBeaches._; - while (node) { - dxl = d3_geom_voronoiLeftBreakPoint(node, directrix) - x; - if (dxl > ε) node = node.L; else { - dxr = x - d3_geom_voronoiRightBreakPoint(node, directrix); - if (dxr > ε) { - if (!node.R) { - lArc = node; - break; - } - node = node.R; - } else { - if (dxl > -ε) { - lArc = node.P; - rArc = node; - } else if (dxr > -ε) { - lArc = node; - rArc = node.N; - } else { - lArc = rArc = node; - } - break; - } - } - } - var newArc = d3_geom_voronoiCreateBeach(site); - d3_geom_voronoiBeaches.insert(lArc, newArc); - if (!lArc && !rArc) return; - if (lArc === rArc) { - d3_geom_voronoiDetachCircle(lArc); - rArc = d3_geom_voronoiCreateBeach(lArc.site); - d3_geom_voronoiBeaches.insert(newArc, rArc); - newArc.edge = rArc.edge = d3_geom_voronoiCreateEdge(lArc.site, newArc.site); - d3_geom_voronoiAttachCircle(lArc); - d3_geom_voronoiAttachCircle(rArc); - return; - } - if (!rArc) { - newArc.edge = d3_geom_voronoiCreateEdge(lArc.site, newArc.site); - return; - } - d3_geom_voronoiDetachCircle(lArc); - d3_geom_voronoiDetachCircle(rArc); - var lSite = lArc.site, ax = lSite.x, ay = lSite.y, bx = site.x - ax, by = site.y - ay, rSite = rArc.site, cx = rSite.x - ax, cy = rSite.y - ay, d = 2 * (bx * cy - by * cx), hb = bx * bx + by * by, hc = cx * cx + cy * cy, vertex = { - x: (cy * hb - by * hc) / d + ax, - y: (bx * hc - cx * hb) / d + ay - }; - d3_geom_voronoiSetEdgeEnd(rArc.edge, lSite, rSite, vertex); - newArc.edge = d3_geom_voronoiCreateEdge(lSite, site, null, vertex); - rArc.edge = d3_geom_voronoiCreateEdge(site, rSite, null, vertex); - d3_geom_voronoiAttachCircle(lArc); - d3_geom_voronoiAttachCircle(rArc); - } - function d3_geom_voronoiLeftBreakPoint(arc, directrix) { - var site = arc.site, rfocx = site.x, rfocy = site.y, pby2 = rfocy - directrix; - if (!pby2) return rfocx; - var lArc = arc.P; - if (!lArc) return -Infinity; - site = lArc.site; - var lfocx = site.x, lfocy = site.y, plby2 = lfocy - directrix; - if (!plby2) return lfocx; - var hl = lfocx - rfocx, aby2 = 1 / pby2 - 1 / plby2, b = hl / plby2; - if (aby2) return (-b + Math.sqrt(b * b - 2 * aby2 * (hl * hl / (-2 * plby2) - lfocy + plby2 / 2 + rfocy - pby2 / 2))) / aby2 + rfocx; - return (rfocx + lfocx) / 2; - } - function d3_geom_voronoiRightBreakPoint(arc, directrix) { - var rArc = arc.N; - if (rArc) return d3_geom_voronoiLeftBreakPoint(rArc, directrix); - var site = arc.site; - return site.y === directrix ? site.x : Infinity; - } - function d3_geom_voronoiCell(site) { - this.site = site; - this.edges = []; - } - d3_geom_voronoiCell.prototype.prepare = function() { - var halfEdges = this.edges, iHalfEdge = halfEdges.length, edge; - while (iHalfEdge--) { - edge = halfEdges[iHalfEdge].edge; - if (!edge.b || !edge.a) halfEdges.splice(iHalfEdge, 1); - } - halfEdges.sort(d3_geom_voronoiHalfEdgeOrder); - return halfEdges.length; - }; - function d3_geom_voronoiCloseCells(extent) { - var x0 = extent[0][0], x1 = extent[1][0], y0 = extent[0][1], y1 = extent[1][1], x2, y2, x3, y3, cells = d3_geom_voronoiCells, iCell = cells.length, cell, iHalfEdge, halfEdges, nHalfEdges, start, end; - while (iCell--) { - cell = cells[iCell]; - if (!cell || !cell.prepare()) continue; - halfEdges = cell.edges; - nHalfEdges = halfEdges.length; - iHalfEdge = 0; - while (iHalfEdge < nHalfEdges) { - end = halfEdges[iHalfEdge].end(), x3 = end.x, y3 = end.y; - start = halfEdges[++iHalfEdge % nHalfEdges].start(), x2 = start.x, y2 = start.y; - if (abs(x3 - x2) > ε || abs(y3 - y2) > ε) { - halfEdges.splice(iHalfEdge, 0, new d3_geom_voronoiHalfEdge(d3_geom_voronoiCreateBorderEdge(cell.site, end, abs(x3 - x0) < ε && y1 - y3 > ε ? { - x: x0, - y: abs(x2 - x0) < ε ? y2 : y1 - } : abs(y3 - y1) < ε && x1 - x3 > ε ? { - x: abs(y2 - y1) < ε ? x2 : x1, - y: y1 - } : abs(x3 - x1) < ε && y3 - y0 > ε ? { - x: x1, - y: abs(x2 - x1) < ε ? y2 : y0 - } : abs(y3 - y0) < ε && x3 - x0 > ε ? { - x: abs(y2 - y0) < ε ? x2 : x0, - y: y0 - } : null), cell.site, null)); - ++nHalfEdges; - } - } - } - } - function d3_geom_voronoiHalfEdgeOrder(a, b) { - return b.angle - a.angle; - } - function d3_geom_voronoiCircle() { - d3_geom_voronoiRedBlackNode(this); - this.x = this.y = this.arc = this.site = this.cy = null; - } - function d3_geom_voronoiAttachCircle(arc) { - var lArc = arc.P, rArc = arc.N; - if (!lArc || !rArc) return; - var lSite = lArc.site, cSite = arc.site, rSite = rArc.site; - if (lSite === rSite) return; - var bx = cSite.x, by = cSite.y, ax = lSite.x - bx, ay = lSite.y - by, cx = rSite.x - bx, cy = rSite.y - by; - var d = 2 * (ax * cy - ay * cx); - if (d >= -ε2) return; - var ha = ax * ax + ay * ay, hc = cx * cx + cy * cy, x = (cy * ha - ay * hc) / d, y = (ax * hc - cx * ha) / d, cy = y + by; - var circle = d3_geom_voronoiCirclePool.pop() || new d3_geom_voronoiCircle(); - circle.arc = arc; - circle.site = cSite; - circle.x = x + bx; - circle.y = cy + Math.sqrt(x * x + y * y); - circle.cy = cy; - arc.circle = circle; - var before = null, node = d3_geom_voronoiCircles._; - while (node) { - if (circle.y < node.y || circle.y === node.y && circle.x <= node.x) { - if (node.L) node = node.L; else { - before = node.P; - break; - } - } else { - if (node.R) node = node.R; else { - before = node; - break; - } - } - } - d3_geom_voronoiCircles.insert(before, circle); - if (!before) d3_geom_voronoiFirstCircle = circle; - } - function d3_geom_voronoiDetachCircle(arc) { - var circle = arc.circle; - if (circle) { - if (!circle.P) d3_geom_voronoiFirstCircle = circle.N; - d3_geom_voronoiCircles.remove(circle); - d3_geom_voronoiCirclePool.push(circle); - d3_geom_voronoiRedBlackNode(circle); - arc.circle = null; - } - } - function d3_geom_voronoiClipEdges(extent) { - var edges = d3_geom_voronoiEdges, clip = d3_geom_clipLine(extent[0][0], extent[0][1], extent[1][0], extent[1][1]), i = edges.length, e; - while (i--) { - e = edges[i]; - if (!d3_geom_voronoiConnectEdge(e, extent) || !clip(e) || abs(e.a.x - e.b.x) < ε && abs(e.a.y - e.b.y) < ε) { - e.a = e.b = null; - edges.splice(i, 1); - } - } - } - function d3_geom_voronoiConnectEdge(edge, extent) { - var vb = edge.b; - if (vb) return true; - var va = edge.a, x0 = extent[0][0], x1 = extent[1][0], y0 = extent[0][1], y1 = extent[1][1], lSite = edge.l, rSite = edge.r, lx = lSite.x, ly = lSite.y, rx = rSite.x, ry = rSite.y, fx = (lx + rx) / 2, fy = (ly + ry) / 2, fm, fb; - if (ry === ly) { - if (fx < x0 || fx >= x1) return; - if (lx > rx) { - if (!va) va = { - x: fx, - y: y0 - }; else if (va.y >= y1) return; - vb = { - x: fx, - y: y1 - }; - } else { - if (!va) va = { - x: fx, - y: y1 - }; else if (va.y < y0) return; - vb = { - x: fx, - y: y0 - }; - } - } else { - fm = (lx - rx) / (ry - ly); - fb = fy - fm * fx; - if (fm < -1 || fm > 1) { - if (lx > rx) { - if (!va) va = { - x: (y0 - fb) / fm, - y: y0 - }; else if (va.y >= y1) return; - vb = { - x: (y1 - fb) / fm, - y: y1 - }; - } else { - if (!va) va = { - x: (y1 - fb) / fm, - y: y1 - }; else if (va.y < y0) return; - vb = { - x: (y0 - fb) / fm, - y: y0 - }; - } - } else { - if (ly < ry) { - if (!va) va = { - x: x0, - y: fm * x0 + fb - }; else if (va.x >= x1) return; - vb = { - x: x1, - y: fm * x1 + fb - }; - } else { - if (!va) va = { - x: x1, - y: fm * x1 + fb - }; else if (va.x < x0) return; - vb = { - x: x0, - y: fm * x0 + fb - }; - } - } - } - edge.a = va; - edge.b = vb; - return true; - } - function d3_geom_voronoiEdge(lSite, rSite) { - this.l = lSite; - this.r = rSite; - this.a = this.b = null; - } - function d3_geom_voronoiCreateEdge(lSite, rSite, va, vb) { - var edge = new d3_geom_voronoiEdge(lSite, rSite); - d3_geom_voronoiEdges.push(edge); - if (va) d3_geom_voronoiSetEdgeEnd(edge, lSite, rSite, va); - if (vb) d3_geom_voronoiSetEdgeEnd(edge, rSite, lSite, vb); - d3_geom_voronoiCells[lSite.i].edges.push(new d3_geom_voronoiHalfEdge(edge, lSite, rSite)); - d3_geom_voronoiCells[rSite.i].edges.push(new d3_geom_voronoiHalfEdge(edge, rSite, lSite)); - return edge; - } - function d3_geom_voronoiCreateBorderEdge(lSite, va, vb) { - var edge = new d3_geom_voronoiEdge(lSite, null); - edge.a = va; - edge.b = vb; - d3_geom_voronoiEdges.push(edge); - return edge; - } - function d3_geom_voronoiSetEdgeEnd(edge, lSite, rSite, vertex) { - if (!edge.a && !edge.b) { - edge.a = vertex; - edge.l = lSite; - edge.r = rSite; - } else if (edge.l === rSite) { - edge.b = vertex; - } else { - edge.a = vertex; - } - } - function d3_geom_voronoiHalfEdge(edge, lSite, rSite) { - var va = edge.a, vb = edge.b; - this.edge = edge; - this.site = lSite; - this.angle = rSite ? Math.atan2(rSite.y - lSite.y, rSite.x - lSite.x) : edge.l === lSite ? Math.atan2(vb.x - va.x, va.y - vb.y) : Math.atan2(va.x - vb.x, vb.y - va.y); - } - d3_geom_voronoiHalfEdge.prototype = { - start: function() { - return this.edge.l === this.site ? this.edge.a : this.edge.b; - }, - end: function() { - return this.edge.l === this.site ? this.edge.b : this.edge.a; - } - }; - function d3_geom_voronoiRedBlackTree() { - this._ = null; - } - function d3_geom_voronoiRedBlackNode(node) { - node.U = node.C = node.L = node.R = node.P = node.N = null; - } - d3_geom_voronoiRedBlackTree.prototype = { - insert: function(after, node) { - var parent, grandpa, uncle; - if (after) { - node.P = after; - node.N = after.N; - if (after.N) after.N.P = node; - after.N = node; - if (after.R) { - after = after.R; - while (after.L) after = after.L; - after.L = node; - } else { - after.R = node; - } - parent = after; - } else if (this._) { - after = d3_geom_voronoiRedBlackFirst(this._); - node.P = null; - node.N = after; - after.P = after.L = node; - parent = after; - } else { - node.P = node.N = null; - this._ = node; - parent = null; - } - node.L = node.R = null; - node.U = parent; - node.C = true; - after = node; - while (parent && parent.C) { - grandpa = parent.U; - if (parent === grandpa.L) { - uncle = grandpa.R; - if (uncle && uncle.C) { - parent.C = uncle.C = false; - grandpa.C = true; - after = grandpa; - } else { - if (after === parent.R) { - d3_geom_voronoiRedBlackRotateLeft(this, parent); - after = parent; - parent = after.U; - } - parent.C = false; - grandpa.C = true; - d3_geom_voronoiRedBlackRotateRight(this, grandpa); - } - } else { - uncle = grandpa.L; - if (uncle && uncle.C) { - parent.C = uncle.C = false; - grandpa.C = true; - after = grandpa; - } else { - if (after === parent.L) { - d3_geom_voronoiRedBlackRotateRight(this, parent); - after = parent; - parent = after.U; - } - parent.C = false; - grandpa.C = true; - d3_geom_voronoiRedBlackRotateLeft(this, grandpa); - } - } - parent = after.U; - } - this._.C = false; - }, - remove: function(node) { - if (node.N) node.N.P = node.P; - if (node.P) node.P.N = node.N; - node.N = node.P = null; - var parent = node.U, sibling, left = node.L, right = node.R, next, red; - if (!left) next = right; else if (!right) next = left; else next = d3_geom_voronoiRedBlackFirst(right); - if (parent) { - if (parent.L === node) parent.L = next; else parent.R = next; - } else { - this._ = next; - } - if (left && right) { - red = next.C; - next.C = node.C; - next.L = left; - left.U = next; - if (next !== right) { - parent = next.U; - next.U = node.U; - node = next.R; - parent.L = node; - next.R = right; - right.U = next; - } else { - next.U = parent; - parent = next; - node = next.R; - } - } else { - red = node.C; - node = next; - } - if (node) node.U = parent; - if (red) return; - if (node && node.C) { - node.C = false; - return; - } - do { - if (node === this._) break; - if (node === parent.L) { - sibling = parent.R; - if (sibling.C) { - sibling.C = false; - parent.C = true; - d3_geom_voronoiRedBlackRotateLeft(this, parent); - sibling = parent.R; - } - if (sibling.L && sibling.L.C || sibling.R && sibling.R.C) { - if (!sibling.R || !sibling.R.C) { - sibling.L.C = false; - sibling.C = true; - d3_geom_voronoiRedBlackRotateRight(this, sibling); - sibling = parent.R; - } - sibling.C = parent.C; - parent.C = sibling.R.C = false; - d3_geom_voronoiRedBlackRotateLeft(this, parent); - node = this._; - break; - } - } else { - sibling = parent.L; - if (sibling.C) { - sibling.C = false; - parent.C = true; - d3_geom_voronoiRedBlackRotateRight(this, parent); - sibling = parent.L; - } - if (sibling.L && sibling.L.C || sibling.R && sibling.R.C) { - if (!sibling.L || !sibling.L.C) { - sibling.R.C = false; - sibling.C = true; - d3_geom_voronoiRedBlackRotateLeft(this, sibling); - sibling = parent.L; - } - sibling.C = parent.C; - parent.C = sibling.L.C = false; - d3_geom_voronoiRedBlackRotateRight(this, parent); - node = this._; - break; - } - } - sibling.C = true; - node = parent; - parent = parent.U; - } while (!node.C); - if (node) node.C = false; - } - }; - function d3_geom_voronoiRedBlackRotateLeft(tree, node) { - var p = node, q = node.R, parent = p.U; - if (parent) { - if (parent.L === p) parent.L = q; else parent.R = q; - } else { - tree._ = q; - } - q.U = parent; - p.U = q; - p.R = q.L; - if (p.R) p.R.U = p; - q.L = p; - } - function d3_geom_voronoiRedBlackRotateRight(tree, node) { - var p = node, q = node.L, parent = p.U; - if (parent) { - if (parent.L === p) parent.L = q; else parent.R = q; - } else { - tree._ = q; - } - q.U = parent; - p.U = q; - p.L = q.R; - if (p.L) p.L.U = p; - q.R = p; - } - function d3_geom_voronoiRedBlackFirst(node) { - while (node.L) node = node.L; - return node; - } - function d3_geom_voronoi(sites, bbox) { - var site = sites.sort(d3_geom_voronoiVertexOrder).pop(), x0, y0, circle; - d3_geom_voronoiEdges = []; - d3_geom_voronoiCells = new Array(sites.length); - d3_geom_voronoiBeaches = new d3_geom_voronoiRedBlackTree(); - d3_geom_voronoiCircles = new d3_geom_voronoiRedBlackTree(); - while (true) { - circle = d3_geom_voronoiFirstCircle; - if (site && (!circle || site.y < circle.y || site.y === circle.y && site.x < circle.x)) { - if (site.x !== x0 || site.y !== y0) { - d3_geom_voronoiCells[site.i] = new d3_geom_voronoiCell(site); - d3_geom_voronoiAddBeach(site); - x0 = site.x, y0 = site.y; - } - site = sites.pop(); - } else if (circle) { - d3_geom_voronoiRemoveBeach(circle.arc); - } else { - break; - } - } - if (bbox) d3_geom_voronoiClipEdges(bbox), d3_geom_voronoiCloseCells(bbox); - var diagram = { - cells: d3_geom_voronoiCells, - edges: d3_geom_voronoiEdges - }; - d3_geom_voronoiBeaches = d3_geom_voronoiCircles = d3_geom_voronoiEdges = d3_geom_voronoiCells = null; - return diagram; - } - function d3_geom_voronoiVertexOrder(a, b) { - return b.y - a.y || b.x - a.x; - } - d3.geom.voronoi = function(points) { - var x = d3_geom_pointX, y = d3_geom_pointY, fx = x, fy = y, clipExtent = d3_geom_voronoiClipExtent; - if (points) return voronoi(points); - function voronoi(data) { - var polygons = new Array(data.length), x0 = clipExtent[0][0], y0 = clipExtent[0][1], x1 = clipExtent[1][0], y1 = clipExtent[1][1]; - d3_geom_voronoi(sites(data), clipExtent).cells.forEach(function(cell, i) { - var edges = cell.edges, site = cell.site, polygon = polygons[i] = edges.length ? edges.map(function(e) { - var s = e.start(); - return [ s.x, s.y ]; - }) : site.x >= x0 && site.x <= x1 && site.y >= y0 && site.y <= y1 ? [ [ x0, y1 ], [ x1, y1 ], [ x1, y0 ], [ x0, y0 ] ] : []; - polygon.point = data[i]; - }); - return polygons; - } - function sites(data) { - return data.map(function(d, i) { - return { - x: Math.round(fx(d, i) / ε) * ε, - y: Math.round(fy(d, i) / ε) * ε, - i: i - }; - }); - } - voronoi.links = function(data) { - return d3_geom_voronoi(sites(data)).edges.filter(function(edge) { - return edge.l && edge.r; - }).map(function(edge) { - return { - source: data[edge.l.i], - target: data[edge.r.i] - }; - }); - }; - voronoi.triangles = function(data) { - var triangles = []; - d3_geom_voronoi(sites(data)).cells.forEach(function(cell, i) { - var site = cell.site, edges = cell.edges.sort(d3_geom_voronoiHalfEdgeOrder), j = -1, m = edges.length, e0, s0, e1 = edges[m - 1].edge, s1 = e1.l === site ? e1.r : e1.l; - while (++j < m) { - e0 = e1; - s0 = s1; - e1 = edges[j].edge; - s1 = e1.l === site ? e1.r : e1.l; - if (i < s0.i && i < s1.i && d3_geom_voronoiTriangleArea(site, s0, s1) < 0) { - triangles.push([ data[i], data[s0.i], data[s1.i] ]); - } - } - }); - return triangles; - }; - voronoi.x = function(_) { - return arguments.length ? (fx = d3_functor(x = _), voronoi) : x; - }; - voronoi.y = function(_) { - return arguments.length ? (fy = d3_functor(y = _), voronoi) : y; - }; - voronoi.clipExtent = function(_) { - if (!arguments.length) return clipExtent === d3_geom_voronoiClipExtent ? null : clipExtent; - clipExtent = _ == null ? d3_geom_voronoiClipExtent : _; - return voronoi; - }; - voronoi.size = function(_) { - if (!arguments.length) return clipExtent === d3_geom_voronoiClipExtent ? null : clipExtent && clipExtent[1]; - return voronoi.clipExtent(_ && [ [ 0, 0 ], _ ]); - }; - return voronoi; - }; - var d3_geom_voronoiClipExtent = [ [ -1e6, -1e6 ], [ 1e6, 1e6 ] ]; - function d3_geom_voronoiTriangleArea(a, b, c) { - return (a.x - c.x) * (b.y - a.y) - (a.x - b.x) * (c.y - a.y); - } - d3.geom.delaunay = function(vertices) { - return d3.geom.voronoi().triangles(vertices); - }; - d3.geom.quadtree = function(points, x1, y1, x2, y2) { - var x = d3_geom_pointX, y = d3_geom_pointY, compat; - if (compat = arguments.length) { - x = d3_geom_quadtreeCompatX; - y = d3_geom_quadtreeCompatY; - if (compat === 3) { - y2 = y1; - x2 = x1; - y1 = x1 = 0; - } - return quadtree(points); - } - function quadtree(data) { - var d, fx = d3_functor(x), fy = d3_functor(y), xs, ys, i, n, x1_, y1_, x2_, y2_; - if (x1 != null) { - x1_ = x1, y1_ = y1, x2_ = x2, y2_ = y2; - } else { - x2_ = y2_ = -(x1_ = y1_ = Infinity); - xs = [], ys = []; - n = data.length; - if (compat) for (i = 0; i < n; ++i) { - d = data[i]; - if (d.x < x1_) x1_ = d.x; - if (d.y < y1_) y1_ = d.y; - if (d.x > x2_) x2_ = d.x; - if (d.y > y2_) y2_ = d.y; - xs.push(d.x); - ys.push(d.y); - } else for (i = 0; i < n; ++i) { - var x_ = +fx(d = data[i], i), y_ = +fy(d, i); - if (x_ < x1_) x1_ = x_; - if (y_ < y1_) y1_ = y_; - if (x_ > x2_) x2_ = x_; - if (y_ > y2_) y2_ = y_; - xs.push(x_); - ys.push(y_); - } - } - var dx = x2_ - x1_, dy = y2_ - y1_; - if (dx > dy) y2_ = y1_ + dx; else x2_ = x1_ + dy; - function insert(n, d, x, y, x1, y1, x2, y2) { - if (isNaN(x) || isNaN(y)) return; - if (n.leaf) { - var nx = n.x, ny = n.y; - if (nx != null) { - if (abs(nx - x) + abs(ny - y) < .01) { - insertChild(n, d, x, y, x1, y1, x2, y2); - } else { - var nPoint = n.point; - n.x = n.y = n.point = null; - insertChild(n, nPoint, nx, ny, x1, y1, x2, y2); - insertChild(n, d, x, y, x1, y1, x2, y2); - } - } else { - n.x = x, n.y = y, n.point = d; - } - } else { - insertChild(n, d, x, y, x1, y1, x2, y2); - } - } - function insertChild(n, d, x, y, x1, y1, x2, y2) { - var sx = (x1 + x2) * .5, sy = (y1 + y2) * .5, right = x >= sx, bottom = y >= sy, i = (bottom << 1) + right; - n.leaf = false; - n = n.nodes[i] || (n.nodes[i] = d3_geom_quadtreeNode()); - if (right) x1 = sx; else x2 = sx; - if (bottom) y1 = sy; else y2 = sy; - insert(n, d, x, y, x1, y1, x2, y2); - } - var root = d3_geom_quadtreeNode(); - root.add = function(d) { - insert(root, d, +fx(d, ++i), +fy(d, i), x1_, y1_, x2_, y2_); - }; - root.visit = function(f) { - d3_geom_quadtreeVisit(f, root, x1_, y1_, x2_, y2_); - }; - i = -1; - if (x1 == null) { - while (++i < n) { - insert(root, data[i], xs[i], ys[i], x1_, y1_, x2_, y2_); - } - --i; - } else data.forEach(root.add); - xs = ys = data = d = null; - return root; - } - quadtree.x = function(_) { - return arguments.length ? (x = _, quadtree) : x; - }; - quadtree.y = function(_) { - return arguments.length ? (y = _, quadtree) : y; - }; - quadtree.extent = function(_) { - if (!arguments.length) return x1 == null ? null : [ [ x1, y1 ], [ x2, y2 ] ]; - if (_ == null) x1 = y1 = x2 = y2 = null; else x1 = +_[0][0], y1 = +_[0][1], x2 = +_[1][0], - y2 = +_[1][1]; - return quadtree; - }; - quadtree.size = function(_) { - if (!arguments.length) return x1 == null ? null : [ x2 - x1, y2 - y1 ]; - if (_ == null) x1 = y1 = x2 = y2 = null; else x1 = y1 = 0, x2 = +_[0], y2 = +_[1]; - return quadtree; - }; - return quadtree; - }; - function d3_geom_quadtreeCompatX(d) { - return d.x; - } - function d3_geom_quadtreeCompatY(d) { - return d.y; - } - function d3_geom_quadtreeNode() { - return { - leaf: true, - nodes: [], - point: null, - x: null, - y: null - }; - } - function d3_geom_quadtreeVisit(f, node, x1, y1, x2, y2) { - if (!f(node, x1, y1, x2, y2)) { - var sx = (x1 + x2) * .5, sy = (y1 + y2) * .5, children = node.nodes; - if (children[0]) d3_geom_quadtreeVisit(f, children[0], x1, y1, sx, sy); - if (children[1]) d3_geom_quadtreeVisit(f, children[1], sx, y1, x2, sy); - if (children[2]) d3_geom_quadtreeVisit(f, children[2], x1, sy, sx, y2); - if (children[3]) d3_geom_quadtreeVisit(f, children[3], sx, sy, x2, y2); - } - } - d3.interpolateRgb = d3_interpolateRgb; - function d3_interpolateRgb(a, b) { - a = d3.rgb(a); - b = d3.rgb(b); - var ar = a.r, ag = a.g, ab = a.b, br = b.r - ar, bg = b.g - ag, bb = b.b - ab; - return function(t) { - return "#" + d3_rgb_hex(Math.round(ar + br * t)) + d3_rgb_hex(Math.round(ag + bg * t)) + d3_rgb_hex(Math.round(ab + bb * t)); - }; - } - d3.interpolateObject = d3_interpolateObject; - function d3_interpolateObject(a, b) { - var i = {}, c = {}, k; - for (k in a) { - if (k in b) { - i[k] = d3_interpolate(a[k], b[k]); - } else { - c[k] = a[k]; - } - } - for (k in b) { - if (!(k in a)) { - c[k] = b[k]; - } - } - return function(t) { - for (k in i) c[k] = i[k](t); - return c; - }; - } - d3.interpolateNumber = d3_interpolateNumber; - function d3_interpolateNumber(a, b) { - b -= a = +a; - return function(t) { - return a + b * t; - }; - } - d3.interpolateString = d3_interpolateString; - function d3_interpolateString(a, b) { - var bi = d3_interpolate_numberA.lastIndex = d3_interpolate_numberB.lastIndex = 0, am, bm, bs, i = -1, s = [], q = []; - a = a + "", b = b + ""; - while ((am = d3_interpolate_numberA.exec(a)) && (bm = d3_interpolate_numberB.exec(b))) { - if ((bs = bm.index) > bi) { - bs = b.substring(bi, bs); - if (s[i]) s[i] += bs; else s[++i] = bs; - } - if ((am = am[0]) === (bm = bm[0])) { - if (s[i]) s[i] += bm; else s[++i] = bm; - } else { - s[++i] = null; - q.push({ - i: i, - x: d3_interpolateNumber(am, bm) - }); - } - bi = d3_interpolate_numberB.lastIndex; - } - if (bi < b.length) { - bs = b.substring(bi); - if (s[i]) s[i] += bs; else s[++i] = bs; - } - return s.length < 2 ? q[0] ? (b = q[0].x, function(t) { - return b(t) + ""; - }) : function() { - return b; - } : (b = q.length, function(t) { - for (var i = 0, o; i < b; ++i) s[(o = q[i]).i] = o.x(t); - return s.join(""); - }); - } - var d3_interpolate_numberA = /[-+]?(?:\d+\.?\d*|\.?\d+)(?:[eE][-+]?\d+)?/g, d3_interpolate_numberB = new RegExp(d3_interpolate_numberA.source, "g"); - d3.interpolate = d3_interpolate; - function d3_interpolate(a, b) { - var i = d3.interpolators.length, f; - while (--i >= 0 && !(f = d3.interpolators[i](a, b))) ; - return f; - } - d3.interpolators = [ function(a, b) { - var t = typeof b; - return (t === "string" ? d3_rgb_names.has(b) || /^(#|rgb\(|hsl\()/.test(b) ? d3_interpolateRgb : d3_interpolateString : b instanceof d3_Color ? d3_interpolateRgb : Array.isArray(b) ? d3_interpolateArray : t === "object" && isNaN(b) ? d3_interpolateObject : d3_interpolateNumber)(a, b); - } ]; - d3.interpolateArray = d3_interpolateArray; - function d3_interpolateArray(a, b) { - var x = [], c = [], na = a.length, nb = b.length, n0 = Math.min(a.length, b.length), i; - for (i = 0; i < n0; ++i) x.push(d3_interpolate(a[i], b[i])); - for (;i < na; ++i) c[i] = a[i]; - for (;i < nb; ++i) c[i] = b[i]; - return function(t) { - for (i = 0; i < n0; ++i) c[i] = x[i](t); - return c; - }; - } - var d3_ease_default = function() { - return d3_identity; - }; - var d3_ease = d3.map({ - linear: d3_ease_default, - poly: d3_ease_poly, - quad: function() { - return d3_ease_quad; - }, - cubic: function() { - return d3_ease_cubic; - }, - sin: function() { - return d3_ease_sin; - }, - exp: function() { - return d3_ease_exp; - }, - circle: function() { - return d3_ease_circle; - }, - elastic: d3_ease_elastic, - back: d3_ease_back, - bounce: function() { - return d3_ease_bounce; - } - }); - var d3_ease_mode = d3.map({ - "in": d3_identity, - out: d3_ease_reverse, - "in-out": d3_ease_reflect, - "out-in": function(f) { - return d3_ease_reflect(d3_ease_reverse(f)); - } - }); - d3.ease = function(name) { - var i = name.indexOf("-"), t = i >= 0 ? name.substring(0, i) : name, m = i >= 0 ? name.substring(i + 1) : "in"; - t = d3_ease.get(t) || d3_ease_default; - m = d3_ease_mode.get(m) || d3_identity; - return d3_ease_clamp(m(t.apply(null, d3_arraySlice.call(arguments, 1)))); - }; - function d3_ease_clamp(f) { - return function(t) { - return t <= 0 ? 0 : t >= 1 ? 1 : f(t); - }; - } - function d3_ease_reverse(f) { - return function(t) { - return 1 - f(1 - t); - }; - } - function d3_ease_reflect(f) { - return function(t) { - return .5 * (t < .5 ? f(2 * t) : 2 - f(2 - 2 * t)); - }; - } - function d3_ease_quad(t) { - return t * t; - } - function d3_ease_cubic(t) { - return t * t * t; - } - function d3_ease_cubicInOut(t) { - if (t <= 0) return 0; - if (t >= 1) return 1; - var t2 = t * t, t3 = t2 * t; - return 4 * (t < .5 ? t3 : 3 * (t - t2) + t3 - .75); - } - function d3_ease_poly(e) { - return function(t) { - return Math.pow(t, e); - }; - } - function d3_ease_sin(t) { - return 1 - Math.cos(t * halfπ); - } - function d3_ease_exp(t) { - return Math.pow(2, 10 * (t - 1)); - } - function d3_ease_circle(t) { - return 1 - Math.sqrt(1 - t * t); - } - function d3_ease_elastic(a, p) { - var s; - if (arguments.length < 2) p = .45; - if (arguments.length) s = p / τ * Math.asin(1 / a); else a = 1, s = p / 4; - return function(t) { - return 1 + a * Math.pow(2, -10 * t) * Math.sin((t - s) * τ / p); - }; - } - function d3_ease_back(s) { - if (!s) s = 1.70158; - return function(t) { - return t * t * ((s + 1) * t - s); - }; - } - function d3_ease_bounce(t) { - return t < 1 / 2.75 ? 7.5625 * t * t : t < 2 / 2.75 ? 7.5625 * (t -= 1.5 / 2.75) * t + .75 : t < 2.5 / 2.75 ? 7.5625 * (t -= 2.25 / 2.75) * t + .9375 : 7.5625 * (t -= 2.625 / 2.75) * t + .984375; - } - d3.interpolateHcl = d3_interpolateHcl; - function d3_interpolateHcl(a, b) { - a = d3.hcl(a); - b = d3.hcl(b); - var ah = a.h, ac = a.c, al = a.l, bh = b.h - ah, bc = b.c - ac, bl = b.l - al; - if (isNaN(bc)) bc = 0, ac = isNaN(ac) ? b.c : ac; - if (isNaN(bh)) bh = 0, ah = isNaN(ah) ? b.h : ah; else if (bh > 180) bh -= 360; else if (bh < -180) bh += 360; - return function(t) { - return d3_hcl_lab(ah + bh * t, ac + bc * t, al + bl * t) + ""; - }; - } - d3.interpolateHsl = d3_interpolateHsl; - function d3_interpolateHsl(a, b) { - a = d3.hsl(a); - b = d3.hsl(b); - var ah = a.h, as = a.s, al = a.l, bh = b.h - ah, bs = b.s - as, bl = b.l - al; - if (isNaN(bs)) bs = 0, as = isNaN(as) ? b.s : as; - if (isNaN(bh)) bh = 0, ah = isNaN(ah) ? b.h : ah; else if (bh > 180) bh -= 360; else if (bh < -180) bh += 360; - return function(t) { - return d3_hsl_rgb(ah + bh * t, as + bs * t, al + bl * t) + ""; - }; - } - d3.interpolateLab = d3_interpolateLab; - function d3_interpolateLab(a, b) { - a = d3.lab(a); - b = d3.lab(b); - var al = a.l, aa = a.a, ab = a.b, bl = b.l - al, ba = b.a - aa, bb = b.b - ab; - return function(t) { - return d3_lab_rgb(al + bl * t, aa + ba * t, ab + bb * t) + ""; - }; - } - d3.interpolateRound = d3_interpolateRound; - function d3_interpolateRound(a, b) { - b -= a; - return function(t) { - return Math.round(a + b * t); - }; - } - d3.transform = function(string) { - var g = d3_document.createElementNS(d3.ns.prefix.svg, "g"); - return (d3.transform = function(string) { - if (string != null) { - g.setAttribute("transform", string); - var t = g.transform.baseVal.consolidate(); - } - return new d3_transform(t ? t.matrix : d3_transformIdentity); - })(string); - }; - function d3_transform(m) { - var r0 = [ m.a, m.b ], r1 = [ m.c, m.d ], kx = d3_transformNormalize(r0), kz = d3_transformDot(r0, r1), ky = d3_transformNormalize(d3_transformCombine(r1, r0, -kz)) || 0; - if (r0[0] * r1[1] < r1[0] * r0[1]) { - r0[0] *= -1; - r0[1] *= -1; - kx *= -1; - kz *= -1; - } - this.rotate = (kx ? Math.atan2(r0[1], r0[0]) : Math.atan2(-r1[0], r1[1])) * d3_degrees; - this.translate = [ m.e, m.f ]; - this.scale = [ kx, ky ]; - this.skew = ky ? Math.atan2(kz, ky) * d3_degrees : 0; - } - d3_transform.prototype.toString = function() { - return "translate(" + this.translate + ")rotate(" + this.rotate + ")skewX(" + this.skew + ")scale(" + this.scale + ")"; - }; - function d3_transformDot(a, b) { - return a[0] * b[0] + a[1] * b[1]; - } - function d3_transformNormalize(a) { - var k = Math.sqrt(d3_transformDot(a, a)); - if (k) { - a[0] /= k; - a[1] /= k; - } - return k; - } - function d3_transformCombine(a, b, k) { - a[0] += k * b[0]; - a[1] += k * b[1]; - return a; - } - var d3_transformIdentity = { - a: 1, - b: 0, - c: 0, - d: 1, - e: 0, - f: 0 - }; - d3.interpolateTransform = d3_interpolateTransform; - function d3_interpolateTransform(a, b) { - var s = [], q = [], n, A = d3.transform(a), B = d3.transform(b), ta = A.translate, tb = B.translate, ra = A.rotate, rb = B.rotate, wa = A.skew, wb = B.skew, ka = A.scale, kb = B.scale; - if (ta[0] != tb[0] || ta[1] != tb[1]) { - s.push("translate(", null, ",", null, ")"); - q.push({ - i: 1, - x: d3_interpolateNumber(ta[0], tb[0]) - }, { - i: 3, - x: d3_interpolateNumber(ta[1], tb[1]) - }); - } else if (tb[0] || tb[1]) { - s.push("translate(" + tb + ")"); - } else { - s.push(""); - } - if (ra != rb) { - if (ra - rb > 180) rb += 360; else if (rb - ra > 180) ra += 360; - q.push({ - i: s.push(s.pop() + "rotate(", null, ")") - 2, - x: d3_interpolateNumber(ra, rb) - }); - } else if (rb) { - s.push(s.pop() + "rotate(" + rb + ")"); - } - if (wa != wb) { - q.push({ - i: s.push(s.pop() + "skewX(", null, ")") - 2, - x: d3_interpolateNumber(wa, wb) - }); - } else if (wb) { - s.push(s.pop() + "skewX(" + wb + ")"); - } - if (ka[0] != kb[0] || ka[1] != kb[1]) { - n = s.push(s.pop() + "scale(", null, ",", null, ")"); - q.push({ - i: n - 4, - x: d3_interpolateNumber(ka[0], kb[0]) - }, { - i: n - 2, - x: d3_interpolateNumber(ka[1], kb[1]) - }); - } else if (kb[0] != 1 || kb[1] != 1) { - s.push(s.pop() + "scale(" + kb + ")"); - } - n = q.length; - return function(t) { - var i = -1, o; - while (++i < n) s[(o = q[i]).i] = o.x(t); - return s.join(""); - }; - } - function d3_uninterpolateNumber(a, b) { - b = b - (a = +a) ? 1 / (b - a) : 0; - return function(x) { - return (x - a) * b; - }; - } - function d3_uninterpolateClamp(a, b) { - b = b - (a = +a) ? 1 / (b - a) : 0; - return function(x) { - return Math.max(0, Math.min(1, (x - a) * b)); - }; - } - d3.layout = {}; - d3.layout.bundle = function() { - return function(links) { - var paths = [], i = -1, n = links.length; - while (++i < n) paths.push(d3_layout_bundlePath(links[i])); - return paths; - }; - }; - function d3_layout_bundlePath(link) { - var start = link.source, end = link.target, lca = d3_layout_bundleLeastCommonAncestor(start, end), points = [ start ]; - while (start !== lca) { - start = start.parent; - points.push(start); - } - var k = points.length; - while (end !== lca) { - points.splice(k, 0, end); - end = end.parent; - } - return points; - } - function d3_layout_bundleAncestors(node) { - var ancestors = [], parent = node.parent; - while (parent != null) { - ancestors.push(node); - node = parent; - parent = parent.parent; - } - ancestors.push(node); - return ancestors; - } - function d3_layout_bundleLeastCommonAncestor(a, b) { - if (a === b) return a; - var aNodes = d3_layout_bundleAncestors(a), bNodes = d3_layout_bundleAncestors(b), aNode = aNodes.pop(), bNode = bNodes.pop(), sharedNode = null; - while (aNode === bNode) { - sharedNode = aNode; - aNode = aNodes.pop(); - bNode = bNodes.pop(); - } - return sharedNode; - } - d3.layout.chord = function() { - var chord = {}, chords, groups, matrix, n, padding = 0, sortGroups, sortSubgroups, sortChords; - function relayout() { - var subgroups = {}, groupSums = [], groupIndex = d3.range(n), subgroupIndex = [], k, x, x0, i, j; - chords = []; - groups = []; - k = 0, i = -1; - while (++i < n) { - x = 0, j = -1; - while (++j < n) { - x += matrix[i][j]; - } - groupSums.push(x); - subgroupIndex.push(d3.range(n)); - k += x; - } - if (sortGroups) { - groupIndex.sort(function(a, b) { - return sortGroups(groupSums[a], groupSums[b]); - }); - } - if (sortSubgroups) { - subgroupIndex.forEach(function(d, i) { - d.sort(function(a, b) { - return sortSubgroups(matrix[i][a], matrix[i][b]); - }); - }); - } - k = (τ - padding * n) / k; - x = 0, i = -1; - while (++i < n) { - x0 = x, j = -1; - while (++j < n) { - var di = groupIndex[i], dj = subgroupIndex[di][j], v = matrix[di][dj], a0 = x, a1 = x += v * k; - subgroups[di + "-" + dj] = { - index: di, - subindex: dj, - startAngle: a0, - endAngle: a1, - value: v - }; - } - groups[di] = { - index: di, - startAngle: x0, - endAngle: x, - value: (x - x0) / k - }; - x += padding; - } - i = -1; - while (++i < n) { - j = i - 1; - while (++j < n) { - var source = subgroups[i + "-" + j], target = subgroups[j + "-" + i]; - if (source.value || target.value) { - chords.push(source.value < target.value ? { - source: target, - target: source - } : { - source: source, - target: target - }); - } - } - } - if (sortChords) resort(); - } - function resort() { - chords.sort(function(a, b) { - return sortChords((a.source.value + a.target.value) / 2, (b.source.value + b.target.value) / 2); - }); - } - chord.matrix = function(x) { - if (!arguments.length) return matrix; - n = (matrix = x) && matrix.length; - chords = groups = null; - return chord; - }; - chord.padding = function(x) { - if (!arguments.length) return padding; - padding = x; - chords = groups = null; - return chord; - }; - chord.sortGroups = function(x) { - if (!arguments.length) return sortGroups; - sortGroups = x; - chords = groups = null; - return chord; - }; - chord.sortSubgroups = function(x) { - if (!arguments.length) return sortSubgroups; - sortSubgroups = x; - chords = null; - return chord; - }; - chord.sortChords = function(x) { - if (!arguments.length) return sortChords; - sortChords = x; - if (chords) resort(); - return chord; - }; - chord.chords = function() { - if (!chords) relayout(); - return chords; - }; - chord.groups = function() { - if (!groups) relayout(); - return groups; - }; - return chord; - }; - d3.layout.force = function() { - var force = {}, event = d3.dispatch("start", "tick", "end"), size = [ 1, 1 ], drag, alpha, friction = .9, linkDistance = d3_layout_forceLinkDistance, linkStrength = d3_layout_forceLinkStrength, charge = -30, chargeDistance2 = d3_layout_forceChargeDistance2, gravity = .1, theta2 = .64, nodes = [], links = [], distances, strengths, charges; - function repulse(node) { - return function(quad, x1, _, x2) { - if (quad.point !== node) { - var dx = quad.cx - node.x, dy = quad.cy - node.y, dw = x2 - x1, dn = dx * dx + dy * dy; - if (dw * dw / theta2 < dn) { - if (dn < chargeDistance2) { - var k = quad.charge / dn; - node.px -= dx * k; - node.py -= dy * k; - } - return true; - } - if (quad.point && dn && dn < chargeDistance2) { - var k = quad.pointCharge / dn; - node.px -= dx * k; - node.py -= dy * k; - } - } - return !quad.charge; - }; - } - force.tick = function() { - if ((alpha *= .99) < .005) { - event.end({ - type: "end", - alpha: alpha = 0 - }); - return true; - } - var n = nodes.length, m = links.length, q, i, o, s, t, l, k, x, y; - for (i = 0; i < m; ++i) { - o = links[i]; - s = o.source; - t = o.target; - x = t.x - s.x; - y = t.y - s.y; - if (l = x * x + y * y) { - l = alpha * strengths[i] * ((l = Math.sqrt(l)) - distances[i]) / l; - x *= l; - y *= l; - t.x -= x * (k = s.weight / (t.weight + s.weight)); - t.y -= y * k; - s.x += x * (k = 1 - k); - s.y += y * k; - } - } - if (k = alpha * gravity) { - x = size[0] / 2; - y = size[1] / 2; - i = -1; - if (k) while (++i < n) { - o = nodes[i]; - o.x += (x - o.x) * k; - o.y += (y - o.y) * k; - } - } - if (charge) { - d3_layout_forceAccumulate(q = d3.geom.quadtree(nodes), alpha, charges); - i = -1; - while (++i < n) { - if (!(o = nodes[i]).fixed) { - q.visit(repulse(o)); - } - } - } - i = -1; - while (++i < n) { - o = nodes[i]; - if (o.fixed) { - o.x = o.px; - o.y = o.py; - } else { - o.x -= (o.px - (o.px = o.x)) * friction; - o.y -= (o.py - (o.py = o.y)) * friction; - } - } - event.tick({ - type: "tick", - alpha: alpha - }); - }; - force.nodes = function(x) { - if (!arguments.length) return nodes; - nodes = x; - return force; - }; - force.links = function(x) { - if (!arguments.length) return links; - links = x; - return force; - }; - force.size = function(x) { - if (!arguments.length) return size; - size = x; - return force; - }; - force.linkDistance = function(x) { - if (!arguments.length) return linkDistance; - linkDistance = typeof x === "function" ? x : +x; - return force; - }; - force.distance = force.linkDistance; - force.linkStrength = function(x) { - if (!arguments.length) return linkStrength; - linkStrength = typeof x === "function" ? x : +x; - return force; - }; - force.friction = function(x) { - if (!arguments.length) return friction; - friction = +x; - return force; - }; - force.charge = function(x) { - if (!arguments.length) return charge; - charge = typeof x === "function" ? x : +x; - return force; - }; - force.chargeDistance = function(x) { - if (!arguments.length) return Math.sqrt(chargeDistance2); - chargeDistance2 = x * x; - return force; - }; - force.gravity = function(x) { - if (!arguments.length) return gravity; - gravity = +x; - return force; - }; - force.theta = function(x) { - if (!arguments.length) return Math.sqrt(theta2); - theta2 = x * x; - return force; - }; - force.alpha = function(x) { - if (!arguments.length) return alpha; - x = +x; - if (alpha) { - if (x > 0) alpha = x; else alpha = 0; - } else if (x > 0) { - event.start({ - type: "start", - alpha: alpha = x - }); - d3.timer(force.tick); - } - return force; - }; - force.start = function() { - var i, n = nodes.length, m = links.length, w = size[0], h = size[1], neighbors, o; - for (i = 0; i < n; ++i) { - (o = nodes[i]).index = i; - o.weight = 0; - } - for (i = 0; i < m; ++i) { - o = links[i]; - if (typeof o.source == "number") o.source = nodes[o.source]; - if (typeof o.target == "number") o.target = nodes[o.target]; - ++o.source.weight; - ++o.target.weight; - } - for (i = 0; i < n; ++i) { - o = nodes[i]; - if (isNaN(o.x)) o.x = position("x", w); - if (isNaN(o.y)) o.y = position("y", h); - if (isNaN(o.px)) o.px = o.x; - if (isNaN(o.py)) o.py = o.y; - } - distances = []; - if (typeof linkDistance === "function") for (i = 0; i < m; ++i) distances[i] = +linkDistance.call(this, links[i], i); else for (i = 0; i < m; ++i) distances[i] = linkDistance; - strengths = []; - if (typeof linkStrength === "function") for (i = 0; i < m; ++i) strengths[i] = +linkStrength.call(this, links[i], i); else for (i = 0; i < m; ++i) strengths[i] = linkStrength; - charges = []; - if (typeof charge === "function") for (i = 0; i < n; ++i) charges[i] = +charge.call(this, nodes[i], i); else for (i = 0; i < n; ++i) charges[i] = charge; - function position(dimension, size) { - if (!neighbors) { - neighbors = new Array(n); - for (j = 0; j < n; ++j) { - neighbors[j] = []; - } - for (j = 0; j < m; ++j) { - var o = links[j]; - neighbors[o.source.index].push(o.target); - neighbors[o.target.index].push(o.source); - } - } - var candidates = neighbors[i], j = -1, m = candidates.length, x; - while (++j < m) if (!isNaN(x = candidates[j][dimension])) return x; - return Math.random() * size; - } - return force.resume(); - }; - force.resume = function() { - return force.alpha(.1); - }; - force.stop = function() { - return force.alpha(0); - }; - force.drag = function() { - if (!drag) drag = d3.behavior.drag().origin(d3_identity).on("dragstart.force", d3_layout_forceDragstart).on("drag.force", dragmove).on("dragend.force", d3_layout_forceDragend); - if (!arguments.length) return drag; - this.on("mouseover.force", d3_layout_forceMouseover).on("mouseout.force", d3_layout_forceMouseout).call(drag); - }; - function dragmove(d) { - d.px = d3.event.x, d.py = d3.event.y; - force.resume(); - } - return d3.rebind(force, event, "on"); - }; - function d3_layout_forceDragstart(d) { - d.fixed |= 2; - } - function d3_layout_forceDragend(d) { - d.fixed &= ~6; - } - function d3_layout_forceMouseover(d) { - d.fixed |= 4; - d.px = d.x, d.py = d.y; - } - function d3_layout_forceMouseout(d) { - d.fixed &= ~4; - } - function d3_layout_forceAccumulate(quad, alpha, charges) { - var cx = 0, cy = 0; - quad.charge = 0; - if (!quad.leaf) { - var nodes = quad.nodes, n = nodes.length, i = -1, c; - while (++i < n) { - c = nodes[i]; - if (c == null) continue; - d3_layout_forceAccumulate(c, alpha, charges); - quad.charge += c.charge; - cx += c.charge * c.cx; - cy += c.charge * c.cy; - } - } - if (quad.point) { - if (!quad.leaf) { - quad.point.x += Math.random() - .5; - quad.point.y += Math.random() - .5; - } - var k = alpha * charges[quad.point.index]; - quad.charge += quad.pointCharge = k; - cx += k * quad.point.x; - cy += k * quad.point.y; - } - quad.cx = cx / quad.charge; - quad.cy = cy / quad.charge; - } - var d3_layout_forceLinkDistance = 20, d3_layout_forceLinkStrength = 1, d3_layout_forceChargeDistance2 = Infinity; - d3.layout.hierarchy = function() { - var sort = d3_layout_hierarchySort, children = d3_layout_hierarchyChildren, value = d3_layout_hierarchyValue; - function hierarchy(root) { - var stack = [ root ], nodes = [], node; - root.depth = 0; - while ((node = stack.pop()) != null) { - nodes.push(node); - if ((childs = children.call(hierarchy, node, node.depth)) && (n = childs.length)) { - var n, childs, child; - while (--n >= 0) { - stack.push(child = childs[n]); - child.parent = node; - child.depth = node.depth + 1; - } - if (value) node.value = 0; - node.children = childs; - } else { - if (value) node.value = +value.call(hierarchy, node, node.depth) || 0; - delete node.children; - } - } - d3_layout_hierarchyVisitAfter(root, function(node) { - var childs, parent; - if (sort && (childs = node.children)) childs.sort(sort); - if (value && (parent = node.parent)) parent.value += node.value; - }); - return nodes; - } - hierarchy.sort = function(x) { - if (!arguments.length) return sort; - sort = x; - return hierarchy; - }; - hierarchy.children = function(x) { - if (!arguments.length) return children; - children = x; - return hierarchy; - }; - hierarchy.value = function(x) { - if (!arguments.length) return value; - value = x; - return hierarchy; - }; - hierarchy.revalue = function(root) { - if (value) { - d3_layout_hierarchyVisitBefore(root, function(node) { - if (node.children) node.value = 0; - }); - d3_layout_hierarchyVisitAfter(root, function(node) { - var parent; - if (!node.children) node.value = +value.call(hierarchy, node, node.depth) || 0; - if (parent = node.parent) parent.value += node.value; - }); - } - return root; - }; - return hierarchy; - }; - function d3_layout_hierarchyRebind(object, hierarchy) { - d3.rebind(object, hierarchy, "sort", "children", "value"); - object.nodes = object; - object.links = d3_layout_hierarchyLinks; - return object; - } - function d3_layout_hierarchyVisitBefore(node, callback) { - var nodes = [ node ]; - while ((node = nodes.pop()) != null) { - callback(node); - if ((children = node.children) && (n = children.length)) { - var n, children; - while (--n >= 0) nodes.push(children[n]); - } - } - } - function d3_layout_hierarchyVisitAfter(node, callback) { - var nodes = [ node ], nodes2 = []; - while ((node = nodes.pop()) != null) { - nodes2.push(node); - if ((children = node.children) && (n = children.length)) { - var i = -1, n, children; - while (++i < n) nodes.push(children[i]); - } - } - while ((node = nodes2.pop()) != null) { - callback(node); - } - } - function d3_layout_hierarchyChildren(d) { - return d.children; - } - function d3_layout_hierarchyValue(d) { - return d.value; - } - function d3_layout_hierarchySort(a, b) { - return b.value - a.value; - } - function d3_layout_hierarchyLinks(nodes) { - return d3.merge(nodes.map(function(parent) { - return (parent.children || []).map(function(child) { - return { - source: parent, - target: child - }; - }); - })); - } - d3.layout.partition = function() { - var hierarchy = d3.layout.hierarchy(), size = [ 1, 1 ]; - function position(node, x, dx, dy) { - var children = node.children; - node.x = x; - node.y = node.depth * dy; - node.dx = dx; - node.dy = dy; - if (children && (n = children.length)) { - var i = -1, n, c, d; - dx = node.value ? dx / node.value : 0; - while (++i < n) { - position(c = children[i], x, d = c.value * dx, dy); - x += d; - } - } - } - function depth(node) { - var children = node.children, d = 0; - if (children && (n = children.length)) { - var i = -1, n; - while (++i < n) d = Math.max(d, depth(children[i])); - } - return 1 + d; - } - function partition(d, i) { - var nodes = hierarchy.call(this, d, i); - position(nodes[0], 0, size[0], size[1] / depth(nodes[0])); - return nodes; - } - partition.size = function(x) { - if (!arguments.length) return size; - size = x; - return partition; - }; - return d3_layout_hierarchyRebind(partition, hierarchy); - }; - d3.layout.pie = function() { - var value = Number, sort = d3_layout_pieSortByValue, startAngle = 0, endAngle = τ; - function pie(data) { - var values = data.map(function(d, i) { - return +value.call(pie, d, i); - }); - var a = +(typeof startAngle === "function" ? startAngle.apply(this, arguments) : startAngle); - var k = ((typeof endAngle === "function" ? endAngle.apply(this, arguments) : endAngle) - a) / d3.sum(values); - var index = d3.range(data.length); - if (sort != null) index.sort(sort === d3_layout_pieSortByValue ? function(i, j) { - return values[j] - values[i]; - } : function(i, j) { - return sort(data[i], data[j]); - }); - var arcs = []; - index.forEach(function(i) { - var d; - arcs[i] = { - data: data[i], - value: d = values[i], - startAngle: a, - endAngle: a += d * k - }; - }); - return arcs; - } - pie.value = function(x) { - if (!arguments.length) return value; - value = x; - return pie; - }; - pie.sort = function(x) { - if (!arguments.length) return sort; - sort = x; - return pie; - }; - pie.startAngle = function(x) { - if (!arguments.length) return startAngle; - startAngle = x; - return pie; - }; - pie.endAngle = function(x) { - if (!arguments.length) return endAngle; - endAngle = x; - return pie; - }; - return pie; - }; - var d3_layout_pieSortByValue = {}; - d3.layout.stack = function() { - var values = d3_identity, order = d3_layout_stackOrderDefault, offset = d3_layout_stackOffsetZero, out = d3_layout_stackOut, x = d3_layout_stackX, y = d3_layout_stackY; - function stack(data, index) { - var series = data.map(function(d, i) { - return values.call(stack, d, i); - }); - var points = series.map(function(d) { - return d.map(function(v, i) { - return [ x.call(stack, v, i), y.call(stack, v, i) ]; - }); - }); - var orders = order.call(stack, points, index); - series = d3.permute(series, orders); - points = d3.permute(points, orders); - var offsets = offset.call(stack, points, index); - var n = series.length, m = series[0].length, i, j, o; - for (j = 0; j < m; ++j) { - out.call(stack, series[0][j], o = offsets[j], points[0][j][1]); - for (i = 1; i < n; ++i) { - out.call(stack, series[i][j], o += points[i - 1][j][1], points[i][j][1]); - } - } - return data; - } - stack.values = function(x) { - if (!arguments.length) return values; - values = x; - return stack; - }; - stack.order = function(x) { - if (!arguments.length) return order; - order = typeof x === "function" ? x : d3_layout_stackOrders.get(x) || d3_layout_stackOrderDefault; - return stack; - }; - stack.offset = function(x) { - if (!arguments.length) return offset; - offset = typeof x === "function" ? x : d3_layout_stackOffsets.get(x) || d3_layout_stackOffsetZero; - return stack; - }; - stack.x = function(z) { - if (!arguments.length) return x; - x = z; - return stack; - }; - stack.y = function(z) { - if (!arguments.length) return y; - y = z; - return stack; - }; - stack.out = function(z) { - if (!arguments.length) return out; - out = z; - return stack; - }; - return stack; - }; - function d3_layout_stackX(d) { - return d.x; - } - function d3_layout_stackY(d) { - return d.y; - } - function d3_layout_stackOut(d, y0, y) { - d.y0 = y0; - d.y = y; - } - var d3_layout_stackOrders = d3.map({ - "inside-out": function(data) { - var n = data.length, i, j, max = data.map(d3_layout_stackMaxIndex), sums = data.map(d3_layout_stackReduceSum), index = d3.range(n).sort(function(a, b) { - return max[a] - max[b]; - }), top = 0, bottom = 0, tops = [], bottoms = []; - for (i = 0; i < n; ++i) { - j = index[i]; - if (top < bottom) { - top += sums[j]; - tops.push(j); - } else { - bottom += sums[j]; - bottoms.push(j); - } - } - return bottoms.reverse().concat(tops); - }, - reverse: function(data) { - return d3.range(data.length).reverse(); - }, - "default": d3_layout_stackOrderDefault - }); - var d3_layout_stackOffsets = d3.map({ - silhouette: function(data) { - var n = data.length, m = data[0].length, sums = [], max = 0, i, j, o, y0 = []; - for (j = 0; j < m; ++j) { - for (i = 0, o = 0; i < n; i++) o += data[i][j][1]; - if (o > max) max = o; - sums.push(o); - } - for (j = 0; j < m; ++j) { - y0[j] = (max - sums[j]) / 2; - } - return y0; - }, - wiggle: function(data) { - var n = data.length, x = data[0], m = x.length, i, j, k, s1, s2, s3, dx, o, o0, y0 = []; - y0[0] = o = o0 = 0; - for (j = 1; j < m; ++j) { - for (i = 0, s1 = 0; i < n; ++i) s1 += data[i][j][1]; - for (i = 0, s2 = 0, dx = x[j][0] - x[j - 1][0]; i < n; ++i) { - for (k = 0, s3 = (data[i][j][1] - data[i][j - 1][1]) / (2 * dx); k < i; ++k) { - s3 += (data[k][j][1] - data[k][j - 1][1]) / dx; - } - s2 += s3 * data[i][j][1]; - } - y0[j] = o -= s1 ? s2 / s1 * dx : 0; - if (o < o0) o0 = o; - } - for (j = 0; j < m; ++j) y0[j] -= o0; - return y0; - }, - expand: function(data) { - var n = data.length, m = data[0].length, k = 1 / n, i, j, o, y0 = []; - for (j = 0; j < m; ++j) { - for (i = 0, o = 0; i < n; i++) o += data[i][j][1]; - if (o) for (i = 0; i < n; i++) data[i][j][1] /= o; else for (i = 0; i < n; i++) data[i][j][1] = k; - } - for (j = 0; j < m; ++j) y0[j] = 0; - return y0; - }, - zero: d3_layout_stackOffsetZero - }); - function d3_layout_stackOrderDefault(data) { - return d3.range(data.length); - } - function d3_layout_stackOffsetZero(data) { - var j = -1, m = data[0].length, y0 = []; - while (++j < m) y0[j] = 0; - return y0; - } - function d3_layout_stackMaxIndex(array) { - var i = 1, j = 0, v = array[0][1], k, n = array.length; - for (;i < n; ++i) { - if ((k = array[i][1]) > v) { - j = i; - v = k; - } - } - return j; - } - function d3_layout_stackReduceSum(d) { - return d.reduce(d3_layout_stackSum, 0); - } - function d3_layout_stackSum(p, d) { - return p + d[1]; - } - d3.layout.histogram = function() { - var frequency = true, valuer = Number, ranger = d3_layout_histogramRange, binner = d3_layout_histogramBinSturges; - function histogram(data, i) { - var bins = [], values = data.map(valuer, this), range = ranger.call(this, values, i), thresholds = binner.call(this, range, values, i), bin, i = -1, n = values.length, m = thresholds.length - 1, k = frequency ? 1 : 1 / n, x; - while (++i < m) { - bin = bins[i] = []; - bin.dx = thresholds[i + 1] - (bin.x = thresholds[i]); - bin.y = 0; - } - if (m > 0) { - i = -1; - while (++i < n) { - x = values[i]; - if (x >= range[0] && x <= range[1]) { - bin = bins[d3.bisect(thresholds, x, 1, m) - 1]; - bin.y += k; - bin.push(data[i]); - } - } - } - return bins; - } - histogram.value = function(x) { - if (!arguments.length) return valuer; - valuer = x; - return histogram; - }; - histogram.range = function(x) { - if (!arguments.length) return ranger; - ranger = d3_functor(x); - return histogram; - }; - histogram.bins = function(x) { - if (!arguments.length) return binner; - binner = typeof x === "number" ? function(range) { - return d3_layout_histogramBinFixed(range, x); - } : d3_functor(x); - return histogram; - }; - histogram.frequency = function(x) { - if (!arguments.length) return frequency; - frequency = !!x; - return histogram; - }; - return histogram; - }; - function d3_layout_histogramBinSturges(range, values) { - return d3_layout_histogramBinFixed(range, Math.ceil(Math.log(values.length) / Math.LN2 + 1)); - } - function d3_layout_histogramBinFixed(range, n) { - var x = -1, b = +range[0], m = (range[1] - b) / n, f = []; - while (++x <= n) f[x] = m * x + b; - return f; - } - function d3_layout_histogramRange(values) { - return [ d3.min(values), d3.max(values) ]; - } - d3.layout.pack = function() { - var hierarchy = d3.layout.hierarchy().sort(d3_layout_packSort), padding = 0, size = [ 1, 1 ], radius; - function pack(d, i) { - var nodes = hierarchy.call(this, d, i), root = nodes[0], w = size[0], h = size[1], r = radius == null ? Math.sqrt : typeof radius === "function" ? radius : function() { - return radius; - }; - root.x = root.y = 0; - d3_layout_hierarchyVisitAfter(root, function(d) { - d.r = +r(d.value); - }); - d3_layout_hierarchyVisitAfter(root, d3_layout_packSiblings); - if (padding) { - var dr = padding * (radius ? 1 : Math.max(2 * root.r / w, 2 * root.r / h)) / 2; - d3_layout_hierarchyVisitAfter(root, function(d) { - d.r += dr; - }); - d3_layout_hierarchyVisitAfter(root, d3_layout_packSiblings); - d3_layout_hierarchyVisitAfter(root, function(d) { - d.r -= dr; - }); - } - d3_layout_packTransform(root, w / 2, h / 2, radius ? 1 : 1 / Math.max(2 * root.r / w, 2 * root.r / h)); - return nodes; - } - pack.size = function(_) { - if (!arguments.length) return size; - size = _; - return pack; - }; - pack.radius = function(_) { - if (!arguments.length) return radius; - radius = _ == null || typeof _ === "function" ? _ : +_; - return pack; - }; - pack.padding = function(_) { - if (!arguments.length) return padding; - padding = +_; - return pack; - }; - return d3_layout_hierarchyRebind(pack, hierarchy); - }; - function d3_layout_packSort(a, b) { - return a.value - b.value; - } - function d3_layout_packInsert(a, b) { - var c = a._pack_next; - a._pack_next = b; - b._pack_prev = a; - b._pack_next = c; - c._pack_prev = b; - } - function d3_layout_packSplice(a, b) { - a._pack_next = b; - b._pack_prev = a; - } - function d3_layout_packIntersects(a, b) { - var dx = b.x - a.x, dy = b.y - a.y, dr = a.r + b.r; - return .999 * dr * dr > dx * dx + dy * dy; - } - function d3_layout_packSiblings(node) { - if (!(nodes = node.children) || !(n = nodes.length)) return; - var nodes, xMin = Infinity, xMax = -Infinity, yMin = Infinity, yMax = -Infinity, a, b, c, i, j, k, n; - function bound(node) { - xMin = Math.min(node.x - node.r, xMin); - xMax = Math.max(node.x + node.r, xMax); - yMin = Math.min(node.y - node.r, yMin); - yMax = Math.max(node.y + node.r, yMax); - } - nodes.forEach(d3_layout_packLink); - a = nodes[0]; - a.x = -a.r; - a.y = 0; - bound(a); - if (n > 1) { - b = nodes[1]; - b.x = b.r; - b.y = 0; - bound(b); - if (n > 2) { - c = nodes[2]; - d3_layout_packPlace(a, b, c); - bound(c); - d3_layout_packInsert(a, c); - a._pack_prev = c; - d3_layout_packInsert(c, b); - b = a._pack_next; - for (i = 3; i < n; i++) { - d3_layout_packPlace(a, b, c = nodes[i]); - var isect = 0, s1 = 1, s2 = 1; - for (j = b._pack_next; j !== b; j = j._pack_next, s1++) { - if (d3_layout_packIntersects(j, c)) { - isect = 1; - break; - } - } - if (isect == 1) { - for (k = a._pack_prev; k !== j._pack_prev; k = k._pack_prev, s2++) { - if (d3_layout_packIntersects(k, c)) { - break; - } - } - } - if (isect) { - if (s1 < s2 || s1 == s2 && b.r < a.r) d3_layout_packSplice(a, b = j); else d3_layout_packSplice(a = k, b); - i--; - } else { - d3_layout_packInsert(a, c); - b = c; - bound(c); - } - } - } - } - var cx = (xMin + xMax) / 2, cy = (yMin + yMax) / 2, cr = 0; - for (i = 0; i < n; i++) { - c = nodes[i]; - c.x -= cx; - c.y -= cy; - cr = Math.max(cr, c.r + Math.sqrt(c.x * c.x + c.y * c.y)); - } - node.r = cr; - nodes.forEach(d3_layout_packUnlink); - } - function d3_layout_packLink(node) { - node._pack_next = node._pack_prev = node; - } - function d3_layout_packUnlink(node) { - delete node._pack_next; - delete node._pack_prev; - } - function d3_layout_packTransform(node, x, y, k) { - var children = node.children; - node.x = x += k * node.x; - node.y = y += k * node.y; - node.r *= k; - if (children) { - var i = -1, n = children.length; - while (++i < n) d3_layout_packTransform(children[i], x, y, k); - } - } - function d3_layout_packPlace(a, b, c) { - var db = a.r + c.r, dx = b.x - a.x, dy = b.y - a.y; - if (db && (dx || dy)) { - var da = b.r + c.r, dc = dx * dx + dy * dy; - da *= da; - db *= db; - var x = .5 + (db - da) / (2 * dc), y = Math.sqrt(Math.max(0, 2 * da * (db + dc) - (db -= dc) * db - da * da)) / (2 * dc); - c.x = a.x + x * dx + y * dy; - c.y = a.y + x * dy - y * dx; - } else { - c.x = a.x + db; - c.y = a.y; - } - } - d3.layout.tree = function() { - var hierarchy = d3.layout.hierarchy().sort(null).value(null), separation = d3_layout_treeSeparation, size = [ 1, 1 ], nodeSize = null; - function tree(d, i) { - var nodes = hierarchy.call(this, d, i), root0 = nodes[0], root1 = wrapTree(root0); - d3_layout_hierarchyVisitAfter(root1, firstWalk), root1.parent.m = -root1.z; - d3_layout_hierarchyVisitBefore(root1, secondWalk); - if (nodeSize) d3_layout_hierarchyVisitBefore(root0, sizeNode); else { - var left = root0, right = root0, bottom = root0; - d3_layout_hierarchyVisitBefore(root0, function(node) { - if (node.x < left.x) left = node; - if (node.x > right.x) right = node; - if (node.depth > bottom.depth) bottom = node; - }); - var tx = separation(left, right) / 2 - left.x, kx = size[0] / (right.x + separation(right, left) / 2 + tx), ky = size[1] / (bottom.depth || 1); - d3_layout_hierarchyVisitBefore(root0, function(node) { - node.x = (node.x + tx) * kx; - node.y = node.depth * ky; - }); - } - return nodes; - } - function wrapTree(root0) { - var root1 = { - A: null, - children: [ root0 ] - }, queue = [ root1 ], node1; - while ((node1 = queue.pop()) != null) { - for (var children = node1.children, child, i = 0, n = children.length; i < n; ++i) { - queue.push((children[i] = child = { - _: children[i], - parent: node1, - children: (child = children[i].children) && child.slice() || [], - A: null, - a: null, - z: 0, - m: 0, - c: 0, - s: 0, - t: null, - i: i - }).a = child); - } - } - return root1.children[0]; - } - function firstWalk(v) { - var children = v.children, siblings = v.parent.children, w = v.i ? siblings[v.i - 1] : null; - if (children.length) { - d3_layout_treeShift(v); - var midpoint = (children[0].z + children[children.length - 1].z) / 2; - if (w) { - v.z = w.z + separation(v._, w._); - v.m = v.z - midpoint; - } else { - v.z = midpoint; - } - } else if (w) { - v.z = w.z + separation(v._, w._); - } - v.parent.A = apportion(v, w, v.parent.A || siblings[0]); - } - function secondWalk(v) { - v._.x = v.z + v.parent.m; - v.m += v.parent.m; - } - function apportion(v, w, ancestor) { - if (w) { - var vip = v, vop = v, vim = w, vom = vip.parent.children[0], sip = vip.m, sop = vop.m, sim = vim.m, som = vom.m, shift; - while (vim = d3_layout_treeRight(vim), vip = d3_layout_treeLeft(vip), vim && vip) { - vom = d3_layout_treeLeft(vom); - vop = d3_layout_treeRight(vop); - vop.a = v; - shift = vim.z + sim - vip.z - sip + separation(vim._, vip._); - if (shift > 0) { - d3_layout_treeMove(d3_layout_treeAncestor(vim, v, ancestor), v, shift); - sip += shift; - sop += shift; - } - sim += vim.m; - sip += vip.m; - som += vom.m; - sop += vop.m; - } - if (vim && !d3_layout_treeRight(vop)) { - vop.t = vim; - vop.m += sim - sop; - } - if (vip && !d3_layout_treeLeft(vom)) { - vom.t = vip; - vom.m += sip - som; - ancestor = v; - } - } - return ancestor; - } - function sizeNode(node) { - node.x *= size[0]; - node.y = node.depth * size[1]; - } - tree.separation = function(x) { - if (!arguments.length) return separation; - separation = x; - return tree; - }; - tree.size = function(x) { - if (!arguments.length) return nodeSize ? null : size; - nodeSize = (size = x) == null ? sizeNode : null; - return tree; - }; - tree.nodeSize = function(x) { - if (!arguments.length) return nodeSize ? size : null; - nodeSize = (size = x) == null ? null : sizeNode; - return tree; - }; - return d3_layout_hierarchyRebind(tree, hierarchy); - }; - function d3_layout_treeSeparation(a, b) { - return a.parent == b.parent ? 1 : 2; - } - function d3_layout_treeLeft(v) { - var children = v.children; - return children.length ? children[0] : v.t; - } - function d3_layout_treeRight(v) { - var children = v.children, n; - return (n = children.length) ? children[n - 1] : v.t; - } - function d3_layout_treeMove(wm, wp, shift) { - var change = shift / (wp.i - wm.i); - wp.c -= change; - wp.s += shift; - wm.c += change; - wp.z += shift; - wp.m += shift; - } - function d3_layout_treeShift(v) { - var shift = 0, change = 0, children = v.children, i = children.length, w; - while (--i >= 0) { - w = children[i]; - w.z += shift; - w.m += shift; - shift += w.s + (change += w.c); - } - } - function d3_layout_treeAncestor(vim, v, ancestor) { - return vim.a.parent === v.parent ? vim.a : ancestor; - } - d3.layout.cluster = function() { - var hierarchy = d3.layout.hierarchy().sort(null).value(null), separation = d3_layout_treeSeparation, size = [ 1, 1 ], nodeSize = false; - function cluster(d, i) { - var nodes = hierarchy.call(this, d, i), root = nodes[0], previousNode, x = 0; - d3_layout_hierarchyVisitAfter(root, function(node) { - var children = node.children; - if (children && children.length) { - node.x = d3_layout_clusterX(children); - node.y = d3_layout_clusterY(children); - } else { - node.x = previousNode ? x += separation(node, previousNode) : 0; - node.y = 0; - previousNode = node; - } - }); - var left = d3_layout_clusterLeft(root), right = d3_layout_clusterRight(root), x0 = left.x - separation(left, right) / 2, x1 = right.x + separation(right, left) / 2; - d3_layout_hierarchyVisitAfter(root, nodeSize ? function(node) { - node.x = (node.x - root.x) * size[0]; - node.y = (root.y - node.y) * size[1]; - } : function(node) { - node.x = (node.x - x0) / (x1 - x0) * size[0]; - node.y = (1 - (root.y ? node.y / root.y : 1)) * size[1]; - }); - return nodes; - } - cluster.separation = function(x) { - if (!arguments.length) return separation; - separation = x; - return cluster; - }; - cluster.size = function(x) { - if (!arguments.length) return nodeSize ? null : size; - nodeSize = (size = x) == null; - return cluster; - }; - cluster.nodeSize = function(x) { - if (!arguments.length) return nodeSize ? size : null; - nodeSize = (size = x) != null; - return cluster; - }; - return d3_layout_hierarchyRebind(cluster, hierarchy); - }; - function d3_layout_clusterY(children) { - return 1 + d3.max(children, function(child) { - return child.y; - }); - } - function d3_layout_clusterX(children) { - return children.reduce(function(x, child) { - return x + child.x; - }, 0) / children.length; - } - function d3_layout_clusterLeft(node) { - var children = node.children; - return children && children.length ? d3_layout_clusterLeft(children[0]) : node; - } - function d3_layout_clusterRight(node) { - var children = node.children, n; - return children && (n = children.length) ? d3_layout_clusterRight(children[n - 1]) : node; - } - d3.layout.treemap = function() { - var hierarchy = d3.layout.hierarchy(), round = Math.round, size = [ 1, 1 ], padding = null, pad = d3_layout_treemapPadNull, sticky = false, stickies, mode = "squarify", ratio = .5 * (1 + Math.sqrt(5)); - function scale(children, k) { - var i = -1, n = children.length, child, area; - while (++i < n) { - area = (child = children[i]).value * (k < 0 ? 0 : k); - child.area = isNaN(area) || area <= 0 ? 0 : area; - } - } - function squarify(node) { - var children = node.children; - if (children && children.length) { - var rect = pad(node), row = [], remaining = children.slice(), child, best = Infinity, score, u = mode === "slice" ? rect.dx : mode === "dice" ? rect.dy : mode === "slice-dice" ? node.depth & 1 ? rect.dy : rect.dx : Math.min(rect.dx, rect.dy), n; - scale(remaining, rect.dx * rect.dy / node.value); - row.area = 0; - while ((n = remaining.length) > 0) { - row.push(child = remaining[n - 1]); - row.area += child.area; - if (mode !== "squarify" || (score = worst(row, u)) <= best) { - remaining.pop(); - best = score; - } else { - row.area -= row.pop().area; - position(row, u, rect, false); - u = Math.min(rect.dx, rect.dy); - row.length = row.area = 0; - best = Infinity; - } - } - if (row.length) { - position(row, u, rect, true); - row.length = row.area = 0; - } - children.forEach(squarify); - } - } - function stickify(node) { - var children = node.children; - if (children && children.length) { - var rect = pad(node), remaining = children.slice(), child, row = []; - scale(remaining, rect.dx * rect.dy / node.value); - row.area = 0; - while (child = remaining.pop()) { - row.push(child); - row.area += child.area; - if (child.z != null) { - position(row, child.z ? rect.dx : rect.dy, rect, !remaining.length); - row.length = row.area = 0; - } - } - children.forEach(stickify); - } - } - function worst(row, u) { - var s = row.area, r, rmax = 0, rmin = Infinity, i = -1, n = row.length; - while (++i < n) { - if (!(r = row[i].area)) continue; - if (r < rmin) rmin = r; - if (r > rmax) rmax = r; - } - s *= s; - u *= u; - return s ? Math.max(u * rmax * ratio / s, s / (u * rmin * ratio)) : Infinity; - } - function position(row, u, rect, flush) { - var i = -1, n = row.length, x = rect.x, y = rect.y, v = u ? round(row.area / u) : 0, o; - if (u == rect.dx) { - if (flush || v > rect.dy) v = rect.dy; - while (++i < n) { - o = row[i]; - o.x = x; - o.y = y; - o.dy = v; - x += o.dx = Math.min(rect.x + rect.dx - x, v ? round(o.area / v) : 0); - } - o.z = true; - o.dx += rect.x + rect.dx - x; - rect.y += v; - rect.dy -= v; - } else { - if (flush || v > rect.dx) v = rect.dx; - while (++i < n) { - o = row[i]; - o.x = x; - o.y = y; - o.dx = v; - y += o.dy = Math.min(rect.y + rect.dy - y, v ? round(o.area / v) : 0); - } - o.z = false; - o.dy += rect.y + rect.dy - y; - rect.x += v; - rect.dx -= v; - } - } - function treemap(d) { - var nodes = stickies || hierarchy(d), root = nodes[0]; - root.x = 0; - root.y = 0; - root.dx = size[0]; - root.dy = size[1]; - if (stickies) hierarchy.revalue(root); - scale([ root ], root.dx * root.dy / root.value); - (stickies ? stickify : squarify)(root); - if (sticky) stickies = nodes; - return nodes; - } - treemap.size = function(x) { - if (!arguments.length) return size; - size = x; - return treemap; - }; - treemap.padding = function(x) { - if (!arguments.length) return padding; - function padFunction(node) { - var p = x.call(treemap, node, node.depth); - return p == null ? d3_layout_treemapPadNull(node) : d3_layout_treemapPad(node, typeof p === "number" ? [ p, p, p, p ] : p); - } - function padConstant(node) { - return d3_layout_treemapPad(node, x); - } - var type; - pad = (padding = x) == null ? d3_layout_treemapPadNull : (type = typeof x) === "function" ? padFunction : type === "number" ? (x = [ x, x, x, x ], - padConstant) : padConstant; - return treemap; - }; - treemap.round = function(x) { - if (!arguments.length) return round != Number; - round = x ? Math.round : Number; - return treemap; - }; - treemap.sticky = function(x) { - if (!arguments.length) return sticky; - sticky = x; - stickies = null; - return treemap; - }; - treemap.ratio = function(x) { - if (!arguments.length) return ratio; - ratio = x; - return treemap; - }; - treemap.mode = function(x) { - if (!arguments.length) return mode; - mode = x + ""; - return treemap; - }; - return d3_layout_hierarchyRebind(treemap, hierarchy); - }; - function d3_layout_treemapPadNull(node) { - return { - x: node.x, - y: node.y, - dx: node.dx, - dy: node.dy - }; - } - function d3_layout_treemapPad(node, padding) { - var x = node.x + padding[3], y = node.y + padding[0], dx = node.dx - padding[1] - padding[3], dy = node.dy - padding[0] - padding[2]; - if (dx < 0) { - x += dx / 2; - dx = 0; - } - if (dy < 0) { - y += dy / 2; - dy = 0; - } - return { - x: x, - y: y, - dx: dx, - dy: dy - }; - } - d3.random = { - normal: function(µ, σ) { - var n = arguments.length; - if (n < 2) σ = 1; - if (n < 1) µ = 0; - return function() { - var x, y, r; - do { - x = Math.random() * 2 - 1; - y = Math.random() * 2 - 1; - r = x * x + y * y; - } while (!r || r > 1); - return µ + σ * x * Math.sqrt(-2 * Math.log(r) / r); - }; - }, - logNormal: function() { - var random = d3.random.normal.apply(d3, arguments); - return function() { - return Math.exp(random()); - }; - }, - bates: function(m) { - var random = d3.random.irwinHall(m); - return function() { - return random() / m; - }; - }, - irwinHall: function(m) { - return function() { - for (var s = 0, j = 0; j < m; j++) s += Math.random(); - return s; - }; - } - }; - d3.scale = {}; - function d3_scaleExtent(domain) { - var start = domain[0], stop = domain[domain.length - 1]; - return start < stop ? [ start, stop ] : [ stop, start ]; - } - function d3_scaleRange(scale) { - return scale.rangeExtent ? scale.rangeExtent() : d3_scaleExtent(scale.range()); - } - function d3_scale_bilinear(domain, range, uninterpolate, interpolate) { - var u = uninterpolate(domain[0], domain[1]), i = interpolate(range[0], range[1]); - return function(x) { - return i(u(x)); - }; - } - function d3_scale_nice(domain, nice) { - var i0 = 0, i1 = domain.length - 1, x0 = domain[i0], x1 = domain[i1], dx; - if (x1 < x0) { - dx = i0, i0 = i1, i1 = dx; - dx = x0, x0 = x1, x1 = dx; - } - domain[i0] = nice.floor(x0); - domain[i1] = nice.ceil(x1); - return domain; - } - function d3_scale_niceStep(step) { - return step ? { - floor: function(x) { - return Math.floor(x / step) * step; - }, - ceil: function(x) { - return Math.ceil(x / step) * step; - } - } : d3_scale_niceIdentity; - } - var d3_scale_niceIdentity = { - floor: d3_identity, - ceil: d3_identity - }; - function d3_scale_polylinear(domain, range, uninterpolate, interpolate) { - var u = [], i = [], j = 0, k = Math.min(domain.length, range.length) - 1; - if (domain[k] < domain[0]) { - domain = domain.slice().reverse(); - range = range.slice().reverse(); - } - while (++j <= k) { - u.push(uninterpolate(domain[j - 1], domain[j])); - i.push(interpolate(range[j - 1], range[j])); - } - return function(x) { - var j = d3.bisect(domain, x, 1, k) - 1; - return i[j](u[j](x)); - }; - } - d3.scale.linear = function() { - return d3_scale_linear([ 0, 1 ], [ 0, 1 ], d3_interpolate, false); - }; - function d3_scale_linear(domain, range, interpolate, clamp) { - var output, input; - function rescale() { - var linear = Math.min(domain.length, range.length) > 2 ? d3_scale_polylinear : d3_scale_bilinear, uninterpolate = clamp ? d3_uninterpolateClamp : d3_uninterpolateNumber; - output = linear(domain, range, uninterpolate, interpolate); - input = linear(range, domain, uninterpolate, d3_interpolate); - return scale; - } - function scale(x) { - return output(x); - } - scale.invert = function(y) { - return input(y); - }; - scale.domain = function(x) { - if (!arguments.length) return domain; - domain = x.map(Number); - return rescale(); - }; - scale.range = function(x) { - if (!arguments.length) return range; - range = x; - return rescale(); - }; - scale.rangeRound = function(x) { - return scale.range(x).interpolate(d3_interpolateRound); - }; - scale.clamp = function(x) { - if (!arguments.length) return clamp; - clamp = x; - return rescale(); - }; - scale.interpolate = function(x) { - if (!arguments.length) return interpolate; - interpolate = x; - return rescale(); - }; - scale.ticks = function(m) { - return d3_scale_linearTicks(domain, m); - }; - scale.tickFormat = function(m, format) { - return d3_scale_linearTickFormat(domain, m, format); - }; - scale.nice = function(m) { - d3_scale_linearNice(domain, m); - return rescale(); - }; - scale.copy = function() { - return d3_scale_linear(domain, range, interpolate, clamp); - }; - return rescale(); - } - function d3_scale_linearRebind(scale, linear) { - return d3.rebind(scale, linear, "range", "rangeRound", "interpolate", "clamp"); - } - function d3_scale_linearNice(domain, m) { - return d3_scale_nice(domain, d3_scale_niceStep(d3_scale_linearTickRange(domain, m)[2])); - } - function d3_scale_linearTickRange(domain, m) { - if (m == null) m = 10; - var extent = d3_scaleExtent(domain), span = extent[1] - extent[0], step = Math.pow(10, Math.floor(Math.log(span / m) / Math.LN10)), err = m / span * step; - if (err <= .15) step *= 10; else if (err <= .35) step *= 5; else if (err <= .75) step *= 2; - extent[0] = Math.ceil(extent[0] / step) * step; - extent[1] = Math.floor(extent[1] / step) * step + step * .5; - extent[2] = step; - return extent; - } - function d3_scale_linearTicks(domain, m) { - return d3.range.apply(d3, d3_scale_linearTickRange(domain, m)); - } - function d3_scale_linearTickFormat(domain, m, format) { - var range = d3_scale_linearTickRange(domain, m); - if (format) { - var match = d3_format_re.exec(format); - match.shift(); - if (match[8] === "s") { - var prefix = d3.formatPrefix(Math.max(abs(range[0]), abs(range[1]))); - if (!match[7]) match[7] = "." + d3_scale_linearPrecision(prefix.scale(range[2])); - match[8] = "f"; - format = d3.format(match.join("")); - return function(d) { - return format(prefix.scale(d)) + prefix.symbol; - }; - } - if (!match[7]) match[7] = "." + d3_scale_linearFormatPrecision(match[8], range); - format = match.join(""); - } else { - format = ",." + d3_scale_linearPrecision(range[2]) + "f"; - } - return d3.format(format); - } - var d3_scale_linearFormatSignificant = { - s: 1, - g: 1, - p: 1, - r: 1, - e: 1 - }; - function d3_scale_linearPrecision(value) { - return -Math.floor(Math.log(value) / Math.LN10 + .01); - } - function d3_scale_linearFormatPrecision(type, range) { - var p = d3_scale_linearPrecision(range[2]); - return type in d3_scale_linearFormatSignificant ? Math.abs(p - d3_scale_linearPrecision(Math.max(abs(range[0]), abs(range[1])))) + +(type !== "e") : p - (type === "%") * 2; - } - d3.scale.log = function() { - return d3_scale_log(d3.scale.linear().domain([ 0, 1 ]), 10, true, [ 1, 10 ]); - }; - function d3_scale_log(linear, base, positive, domain) { - function log(x) { - return (positive ? Math.log(x < 0 ? 0 : x) : -Math.log(x > 0 ? 0 : -x)) / Math.log(base); - } - function pow(x) { - return positive ? Math.pow(base, x) : -Math.pow(base, -x); - } - function scale(x) { - return linear(log(x)); - } - scale.invert = function(x) { - return pow(linear.invert(x)); - }; - scale.domain = function(x) { - if (!arguments.length) return domain; - positive = x[0] >= 0; - linear.domain((domain = x.map(Number)).map(log)); - return scale; - }; - scale.base = function(_) { - if (!arguments.length) return base; - base = +_; - linear.domain(domain.map(log)); - return scale; - }; - scale.nice = function() { - var niced = d3_scale_nice(domain.map(log), positive ? Math : d3_scale_logNiceNegative); - linear.domain(niced); - domain = niced.map(pow); - return scale; - }; - scale.ticks = function() { - var extent = d3_scaleExtent(domain), ticks = [], u = extent[0], v = extent[1], i = Math.floor(log(u)), j = Math.ceil(log(v)), n = base % 1 ? 2 : base; - if (isFinite(j - i)) { - if (positive) { - for (;i < j; i++) for (var k = 1; k < n; k++) ticks.push(pow(i) * k); - ticks.push(pow(i)); - } else { - ticks.push(pow(i)); - for (;i++ < j; ) for (var k = n - 1; k > 0; k--) ticks.push(pow(i) * k); - } - for (i = 0; ticks[i] < u; i++) {} - for (j = ticks.length; ticks[j - 1] > v; j--) {} - ticks = ticks.slice(i, j); - } - return ticks; - }; - scale.tickFormat = function(n, format) { - if (!arguments.length) return d3_scale_logFormat; - if (arguments.length < 2) format = d3_scale_logFormat; else if (typeof format !== "function") format = d3.format(format); - var k = Math.max(.1, n / scale.ticks().length), f = positive ? (e = 1e-12, Math.ceil) : (e = -1e-12, - Math.floor), e; - return function(d) { - return d / pow(f(log(d) + e)) <= k ? format(d) : ""; - }; - }; - scale.copy = function() { - return d3_scale_log(linear.copy(), base, positive, domain); - }; - return d3_scale_linearRebind(scale, linear); - } - var d3_scale_logFormat = d3.format(".0e"), d3_scale_logNiceNegative = { - floor: function(x) { - return -Math.ceil(-x); - }, - ceil: function(x) { - return -Math.floor(-x); - } - }; - d3.scale.pow = function() { - return d3_scale_pow(d3.scale.linear(), 1, [ 0, 1 ]); - }; - function d3_scale_pow(linear, exponent, domain) { - var powp = d3_scale_powPow(exponent), powb = d3_scale_powPow(1 / exponent); - function scale(x) { - return linear(powp(x)); - } - scale.invert = function(x) { - return powb(linear.invert(x)); - }; - scale.domain = function(x) { - if (!arguments.length) return domain; - linear.domain((domain = x.map(Number)).map(powp)); - return scale; - }; - scale.ticks = function(m) { - return d3_scale_linearTicks(domain, m); - }; - scale.tickFormat = function(m, format) { - return d3_scale_linearTickFormat(domain, m, format); - }; - scale.nice = function(m) { - return scale.domain(d3_scale_linearNice(domain, m)); - }; - scale.exponent = function(x) { - if (!arguments.length) return exponent; - powp = d3_scale_powPow(exponent = x); - powb = d3_scale_powPow(1 / exponent); - linear.domain(domain.map(powp)); - return scale; - }; - scale.copy = function() { - return d3_scale_pow(linear.copy(), exponent, domain); - }; - return d3_scale_linearRebind(scale, linear); - } - function d3_scale_powPow(e) { - return function(x) { - return x < 0 ? -Math.pow(-x, e) : Math.pow(x, e); - }; - } - d3.scale.sqrt = function() { - return d3.scale.pow().exponent(.5); - }; - d3.scale.ordinal = function() { - return d3_scale_ordinal([], { - t: "range", - a: [ [] ] - }); - }; - function d3_scale_ordinal(domain, ranger) { - var index, range, rangeBand; - function scale(x) { - return range[((index.get(x) || (ranger.t === "range" ? index.set(x, domain.push(x)) : NaN)) - 1) % range.length]; - } - function steps(start, step) { - return d3.range(domain.length).map(function(i) { - return start + step * i; - }); - } - scale.domain = function(x) { - if (!arguments.length) return domain; - domain = []; - index = new d3_Map(); - var i = -1, n = x.length, xi; - while (++i < n) if (!index.has(xi = x[i])) index.set(xi, domain.push(xi)); - return scale[ranger.t].apply(scale, ranger.a); - }; - scale.range = function(x) { - if (!arguments.length) return range; - range = x; - rangeBand = 0; - ranger = { - t: "range", - a: arguments - }; - return scale; - }; - scale.rangePoints = function(x, padding) { - if (arguments.length < 2) padding = 0; - var start = x[0], stop = x[1], step = (stop - start) / (Math.max(1, domain.length - 1) + padding); - range = steps(domain.length < 2 ? (start + stop) / 2 : start + step * padding / 2, step); - rangeBand = 0; - ranger = { - t: "rangePoints", - a: arguments - }; - return scale; - }; - scale.rangeBands = function(x, padding, outerPadding) { - if (arguments.length < 2) padding = 0; - if (arguments.length < 3) outerPadding = padding; - var reverse = x[1] < x[0], start = x[reverse - 0], stop = x[1 - reverse], step = (stop - start) / (domain.length - padding + 2 * outerPadding); - range = steps(start + step * outerPadding, step); - if (reverse) range.reverse(); - rangeBand = step * (1 - padding); - ranger = { - t: "rangeBands", - a: arguments - }; - return scale; - }; - scale.rangeRoundBands = function(x, padding, outerPadding) { - if (arguments.length < 2) padding = 0; - if (arguments.length < 3) outerPadding = padding; - var reverse = x[1] < x[0], start = x[reverse - 0], stop = x[1 - reverse], step = Math.floor((stop - start) / (domain.length - padding + 2 * outerPadding)), error = stop - start - (domain.length - padding) * step; - range = steps(start + Math.round(error / 2), step); - if (reverse) range.reverse(); - rangeBand = Math.round(step * (1 - padding)); - ranger = { - t: "rangeRoundBands", - a: arguments - }; - return scale; - }; - scale.rangeBand = function() { - return rangeBand; - }; - scale.rangeExtent = function() { - return d3_scaleExtent(ranger.a[0]); - }; - scale.copy = function() { - return d3_scale_ordinal(domain, ranger); - }; - return scale.domain(domain); - } - d3.scale.category10 = function() { - return d3.scale.ordinal().range(d3_category10); - }; - d3.scale.category20 = function() { - return d3.scale.ordinal().range(d3_category20); - }; - d3.scale.category20b = function() { - return d3.scale.ordinal().range(d3_category20b); - }; - d3.scale.category20c = function() { - return d3.scale.ordinal().range(d3_category20c); - }; - var d3_category10 = [ 2062260, 16744206, 2924588, 14034728, 9725885, 9197131, 14907330, 8355711, 12369186, 1556175 ].map(d3_rgbString); - var d3_category20 = [ 2062260, 11454440, 16744206, 16759672, 2924588, 10018698, 14034728, 16750742, 9725885, 12955861, 9197131, 12885140, 14907330, 16234194, 8355711, 13092807, 12369186, 14408589, 1556175, 10410725 ].map(d3_rgbString); - var d3_category20b = [ 3750777, 5395619, 7040719, 10264286, 6519097, 9216594, 11915115, 13556636, 9202993, 12426809, 15186514, 15190932, 8666169, 11356490, 14049643, 15177372, 8077683, 10834324, 13528509, 14589654 ].map(d3_rgbString); - var d3_category20c = [ 3244733, 7057110, 10406625, 13032431, 15095053, 16616764, 16625259, 16634018, 3253076, 7652470, 10607003, 13101504, 7695281, 10394312, 12369372, 14342891, 6513507, 9868950, 12434877, 14277081 ].map(d3_rgbString); - d3.scale.quantile = function() { - return d3_scale_quantile([], []); - }; - function d3_scale_quantile(domain, range) { - var thresholds; - function rescale() { - var k = 0, q = range.length; - thresholds = []; - while (++k < q) thresholds[k - 1] = d3.quantile(domain, k / q); - return scale; - } - function scale(x) { - if (!isNaN(x = +x)) return range[d3.bisect(thresholds, x)]; - } - scale.domain = function(x) { - if (!arguments.length) return domain; - domain = x.filter(d3_number).sort(d3_ascending); - return rescale(); - }; - scale.range = function(x) { - if (!arguments.length) return range; - range = x; - return rescale(); - }; - scale.quantiles = function() { - return thresholds; - }; - scale.invertExtent = function(y) { - y = range.indexOf(y); - return y < 0 ? [ NaN, NaN ] : [ y > 0 ? thresholds[y - 1] : domain[0], y < thresholds.length ? thresholds[y] : domain[domain.length - 1] ]; - }; - scale.copy = function() { - return d3_scale_quantile(domain, range); - }; - return rescale(); - } - d3.scale.quantize = function() { - return d3_scale_quantize(0, 1, [ 0, 1 ]); - }; - function d3_scale_quantize(x0, x1, range) { - var kx, i; - function scale(x) { - return range[Math.max(0, Math.min(i, Math.floor(kx * (x - x0))))]; - } - function rescale() { - kx = range.length / (x1 - x0); - i = range.length - 1; - return scale; - } - scale.domain = function(x) { - if (!arguments.length) return [ x0, x1 ]; - x0 = +x[0]; - x1 = +x[x.length - 1]; - return rescale(); - }; - scale.range = function(x) { - if (!arguments.length) return range; - range = x; - return rescale(); - }; - scale.invertExtent = function(y) { - y = range.indexOf(y); - y = y < 0 ? NaN : y / kx + x0; - return [ y, y + 1 / kx ]; - }; - scale.copy = function() { - return d3_scale_quantize(x0, x1, range); - }; - return rescale(); - } - d3.scale.threshold = function() { - return d3_scale_threshold([ .5 ], [ 0, 1 ]); - }; - function d3_scale_threshold(domain, range) { - function scale(x) { - if (x <= x) return range[d3.bisect(domain, x)]; - } - scale.domain = function(_) { - if (!arguments.length) return domain; - domain = _; - return scale; - }; - scale.range = function(_) { - if (!arguments.length) return range; - range = _; - return scale; - }; - scale.invertExtent = function(y) { - y = range.indexOf(y); - return [ domain[y - 1], domain[y] ]; - }; - scale.copy = function() { - return d3_scale_threshold(domain, range); - }; - return scale; - } - d3.scale.identity = function() { - return d3_scale_identity([ 0, 1 ]); - }; - function d3_scale_identity(domain) { - function identity(x) { - return +x; - } - identity.invert = identity; - identity.domain = identity.range = function(x) { - if (!arguments.length) return domain; - domain = x.map(identity); - return identity; - }; - identity.ticks = function(m) { - return d3_scale_linearTicks(domain, m); - }; - identity.tickFormat = function(m, format) { - return d3_scale_linearTickFormat(domain, m, format); - }; - identity.copy = function() { - return d3_scale_identity(domain); - }; - return identity; - } - d3.svg = {}; - d3.svg.arc = function() { - var innerRadius = d3_svg_arcInnerRadius, outerRadius = d3_svg_arcOuterRadius, startAngle = d3_svg_arcStartAngle, endAngle = d3_svg_arcEndAngle; - function arc() { - var r0 = innerRadius.apply(this, arguments), r1 = outerRadius.apply(this, arguments), a0 = startAngle.apply(this, arguments) + d3_svg_arcOffset, a1 = endAngle.apply(this, arguments) + d3_svg_arcOffset, da = (a1 < a0 && (da = a0, - a0 = a1, a1 = da), a1 - a0), df = da < π ? "0" : "1", c0 = Math.cos(a0), s0 = Math.sin(a0), c1 = Math.cos(a1), s1 = Math.sin(a1); - return da >= d3_svg_arcMax ? r0 ? "M0," + r1 + "A" + r1 + "," + r1 + " 0 1,1 0," + -r1 + "A" + r1 + "," + r1 + " 0 1,1 0," + r1 + "M0," + r0 + "A" + r0 + "," + r0 + " 0 1,0 0," + -r0 + "A" + r0 + "," + r0 + " 0 1,0 0," + r0 + "Z" : "M0," + r1 + "A" + r1 + "," + r1 + " 0 1,1 0," + -r1 + "A" + r1 + "," + r1 + " 0 1,1 0," + r1 + "Z" : r0 ? "M" + r1 * c0 + "," + r1 * s0 + "A" + r1 + "," + r1 + " 0 " + df + ",1 " + r1 * c1 + "," + r1 * s1 + "L" + r0 * c1 + "," + r0 * s1 + "A" + r0 + "," + r0 + " 0 " + df + ",0 " + r0 * c0 + "," + r0 * s0 + "Z" : "M" + r1 * c0 + "," + r1 * s0 + "A" + r1 + "," + r1 + " 0 " + df + ",1 " + r1 * c1 + "," + r1 * s1 + "L0,0" + "Z"; - } - arc.innerRadius = function(v) { - if (!arguments.length) return innerRadius; - innerRadius = d3_functor(v); - return arc; - }; - arc.outerRadius = function(v) { - if (!arguments.length) return outerRadius; - outerRadius = d3_functor(v); - return arc; - }; - arc.startAngle = function(v) { - if (!arguments.length) return startAngle; - startAngle = d3_functor(v); - return arc; - }; - arc.endAngle = function(v) { - if (!arguments.length) return endAngle; - endAngle = d3_functor(v); - return arc; - }; - arc.centroid = function() { - var r = (innerRadius.apply(this, arguments) + outerRadius.apply(this, arguments)) / 2, a = (startAngle.apply(this, arguments) + endAngle.apply(this, arguments)) / 2 + d3_svg_arcOffset; - return [ Math.cos(a) * r, Math.sin(a) * r ]; - }; - return arc; - }; - var d3_svg_arcOffset = -halfπ, d3_svg_arcMax = τ - ε; - function d3_svg_arcInnerRadius(d) { - return d.innerRadius; - } - function d3_svg_arcOuterRadius(d) { - return d.outerRadius; - } - function d3_svg_arcStartAngle(d) { - return d.startAngle; - } - function d3_svg_arcEndAngle(d) { - return d.endAngle; - } - function d3_svg_line(projection) { - var x = d3_geom_pointX, y = d3_geom_pointY, defined = d3_true, interpolate = d3_svg_lineLinear, interpolateKey = interpolate.key, tension = .7; - function line(data) { - var segments = [], points = [], i = -1, n = data.length, d, fx = d3_functor(x), fy = d3_functor(y); - function segment() { - segments.push("M", interpolate(projection(points), tension)); - } - while (++i < n) { - if (defined.call(this, d = data[i], i)) { - points.push([ +fx.call(this, d, i), +fy.call(this, d, i) ]); - } else if (points.length) { - segment(); - points = []; - } - } - if (points.length) segment(); - return segments.length ? segments.join("") : null; - } - line.x = function(_) { - if (!arguments.length) return x; - x = _; - return line; - }; - line.y = function(_) { - if (!arguments.length) return y; - y = _; - return line; - }; - line.defined = function(_) { - if (!arguments.length) return defined; - defined = _; - return line; - }; - line.interpolate = function(_) { - if (!arguments.length) return interpolateKey; - if (typeof _ === "function") interpolateKey = interpolate = _; else interpolateKey = (interpolate = d3_svg_lineInterpolators.get(_) || d3_svg_lineLinear).key; - return line; - }; - line.tension = function(_) { - if (!arguments.length) return tension; - tension = _; - return line; - }; - return line; - } - d3.svg.line = function() { - return d3_svg_line(d3_identity); - }; - var d3_svg_lineInterpolators = d3.map({ - linear: d3_svg_lineLinear, - "linear-closed": d3_svg_lineLinearClosed, - step: d3_svg_lineStep, - "step-before": d3_svg_lineStepBefore, - "step-after": d3_svg_lineStepAfter, - basis: d3_svg_lineBasis, - "basis-open": d3_svg_lineBasisOpen, - "basis-closed": d3_svg_lineBasisClosed, - bundle: d3_svg_lineBundle, - cardinal: d3_svg_lineCardinal, - "cardinal-open": d3_svg_lineCardinalOpen, - "cardinal-closed": d3_svg_lineCardinalClosed, - monotone: d3_svg_lineMonotone - }); - d3_svg_lineInterpolators.forEach(function(key, value) { - value.key = key; - value.closed = /-closed$/.test(key); - }); - function d3_svg_lineLinear(points) { - return points.join("L"); - } - function d3_svg_lineLinearClosed(points) { - return d3_svg_lineLinear(points) + "Z"; - } - function d3_svg_lineStep(points) { - var i = 0, n = points.length, p = points[0], path = [ p[0], ",", p[1] ]; - while (++i < n) path.push("H", (p[0] + (p = points[i])[0]) / 2, "V", p[1]); - if (n > 1) path.push("H", p[0]); - return path.join(""); - } - function d3_svg_lineStepBefore(points) { - var i = 0, n = points.length, p = points[0], path = [ p[0], ",", p[1] ]; - while (++i < n) path.push("V", (p = points[i])[1], "H", p[0]); - return path.join(""); - } - function d3_svg_lineStepAfter(points) { - var i = 0, n = points.length, p = points[0], path = [ p[0], ",", p[1] ]; - while (++i < n) path.push("H", (p = points[i])[0], "V", p[1]); - return path.join(""); - } - function d3_svg_lineCardinalOpen(points, tension) { - return points.length < 4 ? d3_svg_lineLinear(points) : points[1] + d3_svg_lineHermite(points.slice(1, points.length - 1), d3_svg_lineCardinalTangents(points, tension)); - } - function d3_svg_lineCardinalClosed(points, tension) { - return points.length < 3 ? d3_svg_lineLinear(points) : points[0] + d3_svg_lineHermite((points.push(points[0]), - points), d3_svg_lineCardinalTangents([ points[points.length - 2] ].concat(points, [ points[1] ]), tension)); - } - function d3_svg_lineCardinal(points, tension) { - return points.length < 3 ? d3_svg_lineLinear(points) : points[0] + d3_svg_lineHermite(points, d3_svg_lineCardinalTangents(points, tension)); - } - function d3_svg_lineHermite(points, tangents) { - if (tangents.length < 1 || points.length != tangents.length && points.length != tangents.length + 2) { - return d3_svg_lineLinear(points); - } - var quad = points.length != tangents.length, path = "", p0 = points[0], p = points[1], t0 = tangents[0], t = t0, pi = 1; - if (quad) { - path += "Q" + (p[0] - t0[0] * 2 / 3) + "," + (p[1] - t0[1] * 2 / 3) + "," + p[0] + "," + p[1]; - p0 = points[1]; - pi = 2; - } - if (tangents.length > 1) { - t = tangents[1]; - p = points[pi]; - pi++; - path += "C" + (p0[0] + t0[0]) + "," + (p0[1] + t0[1]) + "," + (p[0] - t[0]) + "," + (p[1] - t[1]) + "," + p[0] + "," + p[1]; - for (var i = 2; i < tangents.length; i++, pi++) { - p = points[pi]; - t = tangents[i]; - path += "S" + (p[0] - t[0]) + "," + (p[1] - t[1]) + "," + p[0] + "," + p[1]; - } - } - if (quad) { - var lp = points[pi]; - path += "Q" + (p[0] + t[0] * 2 / 3) + "," + (p[1] + t[1] * 2 / 3) + "," + lp[0] + "," + lp[1]; - } - return path; - } - function d3_svg_lineCardinalTangents(points, tension) { - var tangents = [], a = (1 - tension) / 2, p0, p1 = points[0], p2 = points[1], i = 1, n = points.length; - while (++i < n) { - p0 = p1; - p1 = p2; - p2 = points[i]; - tangents.push([ a * (p2[0] - p0[0]), a * (p2[1] - p0[1]) ]); - } - return tangents; - } - function d3_svg_lineBasis(points) { - if (points.length < 3) return d3_svg_lineLinear(points); - var i = 1, n = points.length, pi = points[0], x0 = pi[0], y0 = pi[1], px = [ x0, x0, x0, (pi = points[1])[0] ], py = [ y0, y0, y0, pi[1] ], path = [ x0, ",", y0, "L", d3_svg_lineDot4(d3_svg_lineBasisBezier3, px), ",", d3_svg_lineDot4(d3_svg_lineBasisBezier3, py) ]; - points.push(points[n - 1]); - while (++i <= n) { - pi = points[i]; - px.shift(); - px.push(pi[0]); - py.shift(); - py.push(pi[1]); - d3_svg_lineBasisBezier(path, px, py); - } - points.pop(); - path.push("L", pi); - return path.join(""); - } - function d3_svg_lineBasisOpen(points) { - if (points.length < 4) return d3_svg_lineLinear(points); - var path = [], i = -1, n = points.length, pi, px = [ 0 ], py = [ 0 ]; - while (++i < 3) { - pi = points[i]; - px.push(pi[0]); - py.push(pi[1]); - } - path.push(d3_svg_lineDot4(d3_svg_lineBasisBezier3, px) + "," + d3_svg_lineDot4(d3_svg_lineBasisBezier3, py)); - --i; - while (++i < n) { - pi = points[i]; - px.shift(); - px.push(pi[0]); - py.shift(); - py.push(pi[1]); - d3_svg_lineBasisBezier(path, px, py); - } - return path.join(""); - } - function d3_svg_lineBasisClosed(points) { - var path, i = -1, n = points.length, m = n + 4, pi, px = [], py = []; - while (++i < 4) { - pi = points[i % n]; - px.push(pi[0]); - py.push(pi[1]); - } - path = [ d3_svg_lineDot4(d3_svg_lineBasisBezier3, px), ",", d3_svg_lineDot4(d3_svg_lineBasisBezier3, py) ]; - --i; - while (++i < m) { - pi = points[i % n]; - px.shift(); - px.push(pi[0]); - py.shift(); - py.push(pi[1]); - d3_svg_lineBasisBezier(path, px, py); - } - return path.join(""); - } - function d3_svg_lineBundle(points, tension) { - var n = points.length - 1; - if (n) { - var x0 = points[0][0], y0 = points[0][1], dx = points[n][0] - x0, dy = points[n][1] - y0, i = -1, p, t; - while (++i <= n) { - p = points[i]; - t = i / n; - p[0] = tension * p[0] + (1 - tension) * (x0 + t * dx); - p[1] = tension * p[1] + (1 - tension) * (y0 + t * dy); - } - } - return d3_svg_lineBasis(points); - } - function d3_svg_lineDot4(a, b) { - return a[0] * b[0] + a[1] * b[1] + a[2] * b[2] + a[3] * b[3]; - } - var d3_svg_lineBasisBezier1 = [ 0, 2 / 3, 1 / 3, 0 ], d3_svg_lineBasisBezier2 = [ 0, 1 / 3, 2 / 3, 0 ], d3_svg_lineBasisBezier3 = [ 0, 1 / 6, 2 / 3, 1 / 6 ]; - function d3_svg_lineBasisBezier(path, x, y) { - path.push("C", d3_svg_lineDot4(d3_svg_lineBasisBezier1, x), ",", d3_svg_lineDot4(d3_svg_lineBasisBezier1, y), ",", d3_svg_lineDot4(d3_svg_lineBasisBezier2, x), ",", d3_svg_lineDot4(d3_svg_lineBasisBezier2, y), ",", d3_svg_lineDot4(d3_svg_lineBasisBezier3, x), ",", d3_svg_lineDot4(d3_svg_lineBasisBezier3, y)); - } - function d3_svg_lineSlope(p0, p1) { - return (p1[1] - p0[1]) / (p1[0] - p0[0]); - } - function d3_svg_lineFiniteDifferences(points) { - var i = 0, j = points.length - 1, m = [], p0 = points[0], p1 = points[1], d = m[0] = d3_svg_lineSlope(p0, p1); - while (++i < j) { - m[i] = (d + (d = d3_svg_lineSlope(p0 = p1, p1 = points[i + 1]))) / 2; - } - m[i] = d; - return m; - } - function d3_svg_lineMonotoneTangents(points) { - var tangents = [], d, a, b, s, m = d3_svg_lineFiniteDifferences(points), i = -1, j = points.length - 1; - while (++i < j) { - d = d3_svg_lineSlope(points[i], points[i + 1]); - if (abs(d) < ε) { - m[i] = m[i + 1] = 0; - } else { - a = m[i] / d; - b = m[i + 1] / d; - s = a * a + b * b; - if (s > 9) { - s = d * 3 / Math.sqrt(s); - m[i] = s * a; - m[i + 1] = s * b; - } - } - } - i = -1; - while (++i <= j) { - s = (points[Math.min(j, i + 1)][0] - points[Math.max(0, i - 1)][0]) / (6 * (1 + m[i] * m[i])); - tangents.push([ s || 0, m[i] * s || 0 ]); - } - return tangents; - } - function d3_svg_lineMonotone(points) { - return points.length < 3 ? d3_svg_lineLinear(points) : points[0] + d3_svg_lineHermite(points, d3_svg_lineMonotoneTangents(points)); - } - d3.svg.line.radial = function() { - var line = d3_svg_line(d3_svg_lineRadial); - line.radius = line.x, delete line.x; - line.angle = line.y, delete line.y; - return line; - }; - function d3_svg_lineRadial(points) { - var point, i = -1, n = points.length, r, a; - while (++i < n) { - point = points[i]; - r = point[0]; - a = point[1] + d3_svg_arcOffset; - point[0] = r * Math.cos(a); - point[1] = r * Math.sin(a); - } - return points; - } - function d3_svg_area(projection) { - var x0 = d3_geom_pointX, x1 = d3_geom_pointX, y0 = 0, y1 = d3_geom_pointY, defined = d3_true, interpolate = d3_svg_lineLinear, interpolateKey = interpolate.key, interpolateReverse = interpolate, L = "L", tension = .7; - function area(data) { - var segments = [], points0 = [], points1 = [], i = -1, n = data.length, d, fx0 = d3_functor(x0), fy0 = d3_functor(y0), fx1 = x0 === x1 ? function() { - return x; - } : d3_functor(x1), fy1 = y0 === y1 ? function() { - return y; - } : d3_functor(y1), x, y; - function segment() { - segments.push("M", interpolate(projection(points1), tension), L, interpolateReverse(projection(points0.reverse()), tension), "Z"); - } - while (++i < n) { - if (defined.call(this, d = data[i], i)) { - points0.push([ x = +fx0.call(this, d, i), y = +fy0.call(this, d, i) ]); - points1.push([ +fx1.call(this, d, i), +fy1.call(this, d, i) ]); - } else if (points0.length) { - segment(); - points0 = []; - points1 = []; - } - } - if (points0.length) segment(); - return segments.length ? segments.join("") : null; - } - area.x = function(_) { - if (!arguments.length) return x1; - x0 = x1 = _; - return area; - }; - area.x0 = function(_) { - if (!arguments.length) return x0; - x0 = _; - return area; - }; - area.x1 = function(_) { - if (!arguments.length) return x1; - x1 = _; - return area; - }; - area.y = function(_) { - if (!arguments.length) return y1; - y0 = y1 = _; - return area; - }; - area.y0 = function(_) { - if (!arguments.length) return y0; - y0 = _; - return area; - }; - area.y1 = function(_) { - if (!arguments.length) return y1; - y1 = _; - return area; - }; - area.defined = function(_) { - if (!arguments.length) return defined; - defined = _; - return area; - }; - area.interpolate = function(_) { - if (!arguments.length) return interpolateKey; - if (typeof _ === "function") interpolateKey = interpolate = _; else interpolateKey = (interpolate = d3_svg_lineInterpolators.get(_) || d3_svg_lineLinear).key; - interpolateReverse = interpolate.reverse || interpolate; - L = interpolate.closed ? "M" : "L"; - return area; - }; - area.tension = function(_) { - if (!arguments.length) return tension; - tension = _; - return area; - }; - return area; - } - d3_svg_lineStepBefore.reverse = d3_svg_lineStepAfter; - d3_svg_lineStepAfter.reverse = d3_svg_lineStepBefore; - d3.svg.area = function() { - return d3_svg_area(d3_identity); - }; - d3.svg.area.radial = function() { - var area = d3_svg_area(d3_svg_lineRadial); - area.radius = area.x, delete area.x; - area.innerRadius = area.x0, delete area.x0; - area.outerRadius = area.x1, delete area.x1; - area.angle = area.y, delete area.y; - area.startAngle = area.y0, delete area.y0; - area.endAngle = area.y1, delete area.y1; - return area; - }; - d3.svg.chord = function() { - var source = d3_source, target = d3_target, radius = d3_svg_chordRadius, startAngle = d3_svg_arcStartAngle, endAngle = d3_svg_arcEndAngle; - function chord(d, i) { - var s = subgroup(this, source, d, i), t = subgroup(this, target, d, i); - return "M" + s.p0 + arc(s.r, s.p1, s.a1 - s.a0) + (equals(s, t) ? curve(s.r, s.p1, s.r, s.p0) : curve(s.r, s.p1, t.r, t.p0) + arc(t.r, t.p1, t.a1 - t.a0) + curve(t.r, t.p1, s.r, s.p0)) + "Z"; - } - function subgroup(self, f, d, i) { - var subgroup = f.call(self, d, i), r = radius.call(self, subgroup, i), a0 = startAngle.call(self, subgroup, i) + d3_svg_arcOffset, a1 = endAngle.call(self, subgroup, i) + d3_svg_arcOffset; - return { - r: r, - a0: a0, - a1: a1, - p0: [ r * Math.cos(a0), r * Math.sin(a0) ], - p1: [ r * Math.cos(a1), r * Math.sin(a1) ] - }; - } - function equals(a, b) { - return a.a0 == b.a0 && a.a1 == b.a1; - } - function arc(r, p, a) { - return "A" + r + "," + r + " 0 " + +(a > π) + ",1 " + p; - } - function curve(r0, p0, r1, p1) { - return "Q 0,0 " + p1; - } - chord.radius = function(v) { - if (!arguments.length) return radius; - radius = d3_functor(v); - return chord; - }; - chord.source = function(v) { - if (!arguments.length) return source; - source = d3_functor(v); - return chord; - }; - chord.target = function(v) { - if (!arguments.length) return target; - target = d3_functor(v); - return chord; - }; - chord.startAngle = function(v) { - if (!arguments.length) return startAngle; - startAngle = d3_functor(v); - return chord; - }; - chord.endAngle = function(v) { - if (!arguments.length) return endAngle; - endAngle = d3_functor(v); - return chord; - }; - return chord; - }; - function d3_svg_chordRadius(d) { - return d.radius; - } - d3.svg.diagonal = function() { - var source = d3_source, target = d3_target, projection = d3_svg_diagonalProjection; - function diagonal(d, i) { - var p0 = source.call(this, d, i), p3 = target.call(this, d, i), m = (p0.y + p3.y) / 2, p = [ p0, { - x: p0.x, - y: m - }, { - x: p3.x, - y: m - }, p3 ]; - p = p.map(projection); - return "M" + p[0] + "C" + p[1] + " " + p[2] + " " + p[3]; - } - diagonal.source = function(x) { - if (!arguments.length) return source; - source = d3_functor(x); - return diagonal; - }; - diagonal.target = function(x) { - if (!arguments.length) return target; - target = d3_functor(x); - return diagonal; - }; - diagonal.projection = function(x) { - if (!arguments.length) return projection; - projection = x; - return diagonal; - }; - return diagonal; - }; - function d3_svg_diagonalProjection(d) { - return [ d.x, d.y ]; - } - d3.svg.diagonal.radial = function() { - var diagonal = d3.svg.diagonal(), projection = d3_svg_diagonalProjection, projection_ = diagonal.projection; - diagonal.projection = function(x) { - return arguments.length ? projection_(d3_svg_diagonalRadialProjection(projection = x)) : projection; - }; - return diagonal; - }; - function d3_svg_diagonalRadialProjection(projection) { - return function() { - var d = projection.apply(this, arguments), r = d[0], a = d[1] + d3_svg_arcOffset; - return [ r * Math.cos(a), r * Math.sin(a) ]; - }; - } - d3.svg.symbol = function() { - var type = d3_svg_symbolType, size = d3_svg_symbolSize; - function symbol(d, i) { - return (d3_svg_symbols.get(type.call(this, d, i)) || d3_svg_symbolCircle)(size.call(this, d, i)); - } - symbol.type = function(x) { - if (!arguments.length) return type; - type = d3_functor(x); - return symbol; - }; - symbol.size = function(x) { - if (!arguments.length) return size; - size = d3_functor(x); - return symbol; - }; - return symbol; - }; - function d3_svg_symbolSize() { - return 64; - } - function d3_svg_symbolType() { - return "circle"; - } - function d3_svg_symbolCircle(size) { - var r = Math.sqrt(size / π); - return "M0," + r + "A" + r + "," + r + " 0 1,1 0," + -r + "A" + r + "," + r + " 0 1,1 0," + r + "Z"; - } - var d3_svg_symbols = d3.map({ - circle: d3_svg_symbolCircle, - cross: function(size) { - var r = Math.sqrt(size / 5) / 2; - return "M" + -3 * r + "," + -r + "H" + -r + "V" + -3 * r + "H" + r + "V" + -r + "H" + 3 * r + "V" + r + "H" + r + "V" + 3 * r + "H" + -r + "V" + r + "H" + -3 * r + "Z"; - }, - diamond: function(size) { - var ry = Math.sqrt(size / (2 * d3_svg_symbolTan30)), rx = ry * d3_svg_symbolTan30; - return "M0," + -ry + "L" + rx + ",0" + " 0," + ry + " " + -rx + ",0" + "Z"; - }, - square: function(size) { - var r = Math.sqrt(size) / 2; - return "M" + -r + "," + -r + "L" + r + "," + -r + " " + r + "," + r + " " + -r + "," + r + "Z"; - }, - "triangle-down": function(size) { - var rx = Math.sqrt(size / d3_svg_symbolSqrt3), ry = rx * d3_svg_symbolSqrt3 / 2; - return "M0," + ry + "L" + rx + "," + -ry + " " + -rx + "," + -ry + "Z"; - }, - "triangle-up": function(size) { - var rx = Math.sqrt(size / d3_svg_symbolSqrt3), ry = rx * d3_svg_symbolSqrt3 / 2; - return "M0," + -ry + "L" + rx + "," + ry + " " + -rx + "," + ry + "Z"; - } - }); - d3.svg.symbolTypes = d3_svg_symbols.keys(); - var d3_svg_symbolSqrt3 = Math.sqrt(3), d3_svg_symbolTan30 = Math.tan(30 * d3_radians); - function d3_transition(groups, id) { - d3_subclass(groups, d3_transitionPrototype); - groups.id = id; - return groups; - } - var d3_transitionPrototype = [], d3_transitionId = 0, d3_transitionInheritId, d3_transitionInherit; - d3_transitionPrototype.call = d3_selectionPrototype.call; - d3_transitionPrototype.empty = d3_selectionPrototype.empty; - d3_transitionPrototype.node = d3_selectionPrototype.node; - d3_transitionPrototype.size = d3_selectionPrototype.size; - d3.transition = function(selection) { - return arguments.length ? d3_transitionInheritId ? selection.transition() : selection : d3_selectionRoot.transition(); - }; - d3.transition.prototype = d3_transitionPrototype; - d3_transitionPrototype.select = function(selector) { - var id = this.id, subgroups = [], subgroup, subnode, node; - selector = d3_selection_selector(selector); - for (var j = -1, m = this.length; ++j < m; ) { - subgroups.push(subgroup = []); - for (var group = this[j], i = -1, n = group.length; ++i < n; ) { - if ((node = group[i]) && (subnode = selector.call(node, node.__data__, i, j))) { - if ("__data__" in node) subnode.__data__ = node.__data__; - d3_transitionNode(subnode, i, id, node.__transition__[id]); - subgroup.push(subnode); - } else { - subgroup.push(null); - } - } - } - return d3_transition(subgroups, id); - }; - d3_transitionPrototype.selectAll = function(selector) { - var id = this.id, subgroups = [], subgroup, subnodes, node, subnode, transition; - selector = d3_selection_selectorAll(selector); - for (var j = -1, m = this.length; ++j < m; ) { - for (var group = this[j], i = -1, n = group.length; ++i < n; ) { - if (node = group[i]) { - transition = node.__transition__[id]; - subnodes = selector.call(node, node.__data__, i, j); - subgroups.push(subgroup = []); - for (var k = -1, o = subnodes.length; ++k < o; ) { - if (subnode = subnodes[k]) d3_transitionNode(subnode, k, id, transition); - subgroup.push(subnode); - } - } - } - } - return d3_transition(subgroups, id); - }; - d3_transitionPrototype.filter = function(filter) { - var subgroups = [], subgroup, group, node; - if (typeof filter !== "function") filter = d3_selection_filter(filter); - for (var j = 0, m = this.length; j < m; j++) { - subgroups.push(subgroup = []); - for (var group = this[j], i = 0, n = group.length; i < n; i++) { - if ((node = group[i]) && filter.call(node, node.__data__, i, j)) { - subgroup.push(node); - } - } - } - return d3_transition(subgroups, this.id); - }; - d3_transitionPrototype.tween = function(name, tween) { - var id = this.id; - if (arguments.length < 2) return this.node().__transition__[id].tween.get(name); - return d3_selection_each(this, tween == null ? function(node) { - node.__transition__[id].tween.remove(name); - } : function(node) { - node.__transition__[id].tween.set(name, tween); - }); - }; - function d3_transition_tween(groups, name, value, tween) { - var id = groups.id; - return d3_selection_each(groups, typeof value === "function" ? function(node, i, j) { - node.__transition__[id].tween.set(name, tween(value.call(node, node.__data__, i, j))); - } : (value = tween(value), function(node) { - node.__transition__[id].tween.set(name, value); - })); - } - d3_transitionPrototype.attr = function(nameNS, value) { - if (arguments.length < 2) { - for (value in nameNS) this.attr(value, nameNS[value]); - return this; - } - var interpolate = nameNS == "transform" ? d3_interpolateTransform : d3_interpolate, name = d3.ns.qualify(nameNS); - function attrNull() { - this.removeAttribute(name); - } - function attrNullNS() { - this.removeAttributeNS(name.space, name.local); - } - function attrTween(b) { - return b == null ? attrNull : (b += "", function() { - var a = this.getAttribute(name), i; - return a !== b && (i = interpolate(a, b), function(t) { - this.setAttribute(name, i(t)); - }); - }); - } - function attrTweenNS(b) { - return b == null ? attrNullNS : (b += "", function() { - var a = this.getAttributeNS(name.space, name.local), i; - return a !== b && (i = interpolate(a, b), function(t) { - this.setAttributeNS(name.space, name.local, i(t)); - }); - }); - } - return d3_transition_tween(this, "attr." + nameNS, value, name.local ? attrTweenNS : attrTween); - }; - d3_transitionPrototype.attrTween = function(nameNS, tween) { - var name = d3.ns.qualify(nameNS); - function attrTween(d, i) { - var f = tween.call(this, d, i, this.getAttribute(name)); - return f && function(t) { - this.setAttribute(name, f(t)); - }; - } - function attrTweenNS(d, i) { - var f = tween.call(this, d, i, this.getAttributeNS(name.space, name.local)); - return f && function(t) { - this.setAttributeNS(name.space, name.local, f(t)); - }; - } - return this.tween("attr." + nameNS, name.local ? attrTweenNS : attrTween); - }; - d3_transitionPrototype.style = function(name, value, priority) { - var n = arguments.length; - if (n < 3) { - if (typeof name !== "string") { - if (n < 2) value = ""; - for (priority in name) this.style(priority, name[priority], value); - return this; - } - priority = ""; - } - function styleNull() { - this.style.removeProperty(name); - } - function styleString(b) { - return b == null ? styleNull : (b += "", function() { - var a = d3_window.getComputedStyle(this, null).getPropertyValue(name), i; - return a !== b && (i = d3_interpolate(a, b), function(t) { - this.style.setProperty(name, i(t), priority); - }); - }); - } - return d3_transition_tween(this, "style." + name, value, styleString); - }; - d3_transitionPrototype.styleTween = function(name, tween, priority) { - if (arguments.length < 3) priority = ""; - function styleTween(d, i) { - var f = tween.call(this, d, i, d3_window.getComputedStyle(this, null).getPropertyValue(name)); - return f && function(t) { - this.style.setProperty(name, f(t), priority); - }; - } - return this.tween("style." + name, styleTween); - }; - d3_transitionPrototype.text = function(value) { - return d3_transition_tween(this, "text", value, d3_transition_text); - }; - function d3_transition_text(b) { - if (b == null) b = ""; - return function() { - this.textContent = b; - }; - } - d3_transitionPrototype.remove = function() { - return this.each("end.transition", function() { - var p; - if (this.__transition__.count < 2 && (p = this.parentNode)) p.removeChild(this); - }); - }; - d3_transitionPrototype.ease = function(value) { - var id = this.id; - if (arguments.length < 1) return this.node().__transition__[id].ease; - if (typeof value !== "function") value = d3.ease.apply(d3, arguments); - return d3_selection_each(this, function(node) { - node.__transition__[id].ease = value; - }); - }; - d3_transitionPrototype.delay = function(value) { - var id = this.id; - if (arguments.length < 1) return this.node().__transition__[id].delay; - return d3_selection_each(this, typeof value === "function" ? function(node, i, j) { - node.__transition__[id].delay = +value.call(node, node.__data__, i, j); - } : (value = +value, function(node) { - node.__transition__[id].delay = value; - })); - }; - d3_transitionPrototype.duration = function(value) { - var id = this.id; - if (arguments.length < 1) return this.node().__transition__[id].duration; - return d3_selection_each(this, typeof value === "function" ? function(node, i, j) { - node.__transition__[id].duration = Math.max(1, value.call(node, node.__data__, i, j)); - } : (value = Math.max(1, value), function(node) { - node.__transition__[id].duration = value; - })); - }; - d3_transitionPrototype.each = function(type, listener) { - var id = this.id; - if (arguments.length < 2) { - var inherit = d3_transitionInherit, inheritId = d3_transitionInheritId; - d3_transitionInheritId = id; - d3_selection_each(this, function(node, i, j) { - d3_transitionInherit = node.__transition__[id]; - type.call(node, node.__data__, i, j); - }); - d3_transitionInherit = inherit; - d3_transitionInheritId = inheritId; - } else { - d3_selection_each(this, function(node) { - var transition = node.__transition__[id]; - (transition.event || (transition.event = d3.dispatch("start", "end"))).on(type, listener); - }); - } - return this; - }; - d3_transitionPrototype.transition = function() { - var id0 = this.id, id1 = ++d3_transitionId, subgroups = [], subgroup, group, node, transition; - for (var j = 0, m = this.length; j < m; j++) { - subgroups.push(subgroup = []); - for (var group = this[j], i = 0, n = group.length; i < n; i++) { - if (node = group[i]) { - transition = Object.create(node.__transition__[id0]); - transition.delay += transition.duration; - d3_transitionNode(node, i, id1, transition); - } - subgroup.push(node); - } - } - return d3_transition(subgroups, id1); - }; - function d3_transitionNode(node, i, id, inherit) { - var lock = node.__transition__ || (node.__transition__ = { - active: 0, - count: 0 - }), transition = lock[id]; - if (!transition) { - var time = inherit.time; - transition = lock[id] = { - tween: new d3_Map(), - time: time, - ease: inherit.ease, - delay: inherit.delay, - duration: inherit.duration - }; - ++lock.count; - d3.timer(function(elapsed) { - var d = node.__data__, ease = transition.ease, delay = transition.delay, duration = transition.duration, timer = d3_timer_active, tweened = []; - timer.t = delay + time; - if (delay <= elapsed) return start(elapsed - delay); - timer.c = start; - function start(elapsed) { - if (lock.active > id) return stop(); - lock.active = id; - transition.event && transition.event.start.call(node, d, i); - transition.tween.forEach(function(key, value) { - if (value = value.call(node, d, i)) { - tweened.push(value); - } - }); - d3.timer(function() { - timer.c = tick(elapsed || 1) ? d3_true : tick; - return 1; - }, 0, time); - } - function tick(elapsed) { - if (lock.active !== id) return stop(); - var t = elapsed / duration, e = ease(t), n = tweened.length; - while (n > 0) { - tweened[--n].call(node, e); - } - if (t >= 1) { - transition.event && transition.event.end.call(node, d, i); - return stop(); - } - } - function stop() { - if (--lock.count) delete lock[id]; else delete node.__transition__; - return 1; - } - }, 0, time); - } - } - d3.svg.axis = function() { - var scale = d3.scale.linear(), orient = d3_svg_axisDefaultOrient, innerTickSize = 6, outerTickSize = 6, tickPadding = 3, tickArguments_ = [ 10 ], tickValues = null, tickFormat_; - function axis(g) { - g.each(function() { - var g = d3.select(this); - var scale0 = this.__chart__ || scale, scale1 = this.__chart__ = scale.copy(); - var ticks = tickValues == null ? scale1.ticks ? scale1.ticks.apply(scale1, tickArguments_) : scale1.domain() : tickValues, tickFormat = tickFormat_ == null ? scale1.tickFormat ? scale1.tickFormat.apply(scale1, tickArguments_) : d3_identity : tickFormat_, tick = g.selectAll(".tick").data(ticks, scale1), tickEnter = tick.enter().insert("g", ".domain").attr("class", "tick").style("opacity", ε), tickExit = d3.transition(tick.exit()).style("opacity", ε).remove(), tickUpdate = d3.transition(tick.order()).style("opacity", 1), tickTransform; - var range = d3_scaleRange(scale1), path = g.selectAll(".domain").data([ 0 ]), pathUpdate = (path.enter().append("path").attr("class", "domain"), - d3.transition(path)); - tickEnter.append("line"); - tickEnter.append("text"); - var lineEnter = tickEnter.select("line"), lineUpdate = tickUpdate.select("line"), text = tick.select("text").text(tickFormat), textEnter = tickEnter.select("text"), textUpdate = tickUpdate.select("text"); - switch (orient) { - case "bottom": - { - tickTransform = d3_svg_axisX; - lineEnter.attr("y2", innerTickSize); - textEnter.attr("y", Math.max(innerTickSize, 0) + tickPadding); - lineUpdate.attr("x2", 0).attr("y2", innerTickSize); - textUpdate.attr("x", 0).attr("y", Math.max(innerTickSize, 0) + tickPadding); - text.attr("dy", ".71em").style("text-anchor", "middle"); - pathUpdate.attr("d", "M" + range[0] + "," + outerTickSize + "V0H" + range[1] + "V" + outerTickSize); - break; - } - - case "top": - { - tickTransform = d3_svg_axisX; - lineEnter.attr("y2", -innerTickSize); - textEnter.attr("y", -(Math.max(innerTickSize, 0) + tickPadding)); - lineUpdate.attr("x2", 0).attr("y2", -innerTickSize); - textUpdate.attr("x", 0).attr("y", -(Math.max(innerTickSize, 0) + tickPadding)); - text.attr("dy", "0em").style("text-anchor", "middle"); - pathUpdate.attr("d", "M" + range[0] + "," + -outerTickSize + "V0H" + range[1] + "V" + -outerTickSize); - break; - } - - case "left": - { - tickTransform = d3_svg_axisY; - lineEnter.attr("x2", -innerTickSize); - textEnter.attr("x", -(Math.max(innerTickSize, 0) + tickPadding)); - lineUpdate.attr("x2", -innerTickSize).attr("y2", 0); - textUpdate.attr("x", -(Math.max(innerTickSize, 0) + tickPadding)).attr("y", 0); - text.attr("dy", ".32em").style("text-anchor", "end"); - pathUpdate.attr("d", "M" + -outerTickSize + "," + range[0] + "H0V" + range[1] + "H" + -outerTickSize); - break; - } - - case "right": - { - tickTransform = d3_svg_axisY; - lineEnter.attr("x2", innerTickSize); - textEnter.attr("x", Math.max(innerTickSize, 0) + tickPadding); - lineUpdate.attr("x2", innerTickSize).attr("y2", 0); - textUpdate.attr("x", Math.max(innerTickSize, 0) + tickPadding).attr("y", 0); - text.attr("dy", ".32em").style("text-anchor", "start"); - pathUpdate.attr("d", "M" + outerTickSize + "," + range[0] + "H0V" + range[1] + "H" + outerTickSize); - break; - } - } - if (scale1.rangeBand) { - var x = scale1, dx = x.rangeBand() / 2; - scale0 = scale1 = function(d) { - return x(d) + dx; - }; - } else if (scale0.rangeBand) { - scale0 = scale1; - } else { - tickExit.call(tickTransform, scale1); - } - tickEnter.call(tickTransform, scale0); - tickUpdate.call(tickTransform, scale1); - }); - } - axis.scale = function(x) { - if (!arguments.length) return scale; - scale = x; - return axis; - }; - axis.orient = function(x) { - if (!arguments.length) return orient; - orient = x in d3_svg_axisOrients ? x + "" : d3_svg_axisDefaultOrient; - return axis; - }; - axis.ticks = function() { - if (!arguments.length) return tickArguments_; - tickArguments_ = arguments; - return axis; - }; - axis.tickValues = function(x) { - if (!arguments.length) return tickValues; - tickValues = x; - return axis; - }; - axis.tickFormat = function(x) { - if (!arguments.length) return tickFormat_; - tickFormat_ = x; - return axis; - }; - axis.tickSize = function(x) { - var n = arguments.length; - if (!n) return innerTickSize; - innerTickSize = +x; - outerTickSize = +arguments[n - 1]; - return axis; - }; - axis.innerTickSize = function(x) { - if (!arguments.length) return innerTickSize; - innerTickSize = +x; - return axis; - }; - axis.outerTickSize = function(x) { - if (!arguments.length) return outerTickSize; - outerTickSize = +x; - return axis; - }; - axis.tickPadding = function(x) { - if (!arguments.length) return tickPadding; - tickPadding = +x; - return axis; - }; - axis.tickSubdivide = function() { - return arguments.length && axis; - }; - return axis; - }; - var d3_svg_axisDefaultOrient = "bottom", d3_svg_axisOrients = { - top: 1, - right: 1, - bottom: 1, - left: 1 - }; - function d3_svg_axisX(selection, x) { - selection.attr("transform", function(d) { - return "translate(" + x(d) + ",0)"; - }); - } - function d3_svg_axisY(selection, y) { - selection.attr("transform", function(d) { - return "translate(0," + y(d) + ")"; - }); - } - d3.svg.brush = function() { - var event = d3_eventDispatch(brush, "brushstart", "brush", "brushend"), x = null, y = null, xExtent = [ 0, 0 ], yExtent = [ 0, 0 ], xExtentDomain, yExtentDomain, xClamp = true, yClamp = true, resizes = d3_svg_brushResizes[0]; - function brush(g) { - g.each(function() { - var g = d3.select(this).style("pointer-events", "all").style("-webkit-tap-highlight-color", "rgba(0,0,0,0)").on("mousedown.brush", brushstart).on("touchstart.brush", brushstart); - var background = g.selectAll(".background").data([ 0 ]); - background.enter().append("rect").attr("class", "background").style("visibility", "hidden").style("cursor", "crosshair"); - g.selectAll(".extent").data([ 0 ]).enter().append("rect").attr("class", "extent").style("cursor", "move"); - var resize = g.selectAll(".resize").data(resizes, d3_identity); - resize.exit().remove(); - resize.enter().append("g").attr("class", function(d) { - return "resize " + d; - }).style("cursor", function(d) { - return d3_svg_brushCursor[d]; - }).append("rect").attr("x", function(d) { - return /[ew]$/.test(d) ? -3 : null; - }).attr("y", function(d) { - return /^[ns]/.test(d) ? -3 : null; - }).attr("width", 6).attr("height", 6).style("visibility", "hidden"); - resize.style("display", brush.empty() ? "none" : null); - var gUpdate = d3.transition(g), backgroundUpdate = d3.transition(background), range; - if (x) { - range = d3_scaleRange(x); - backgroundUpdate.attr("x", range[0]).attr("width", range[1] - range[0]); - redrawX(gUpdate); - } - if (y) { - range = d3_scaleRange(y); - backgroundUpdate.attr("y", range[0]).attr("height", range[1] - range[0]); - redrawY(gUpdate); - } - redraw(gUpdate); - }); - } - brush.event = function(g) { - g.each(function() { - var event_ = event.of(this, arguments), extent1 = { - x: xExtent, - y: yExtent, - i: xExtentDomain, - j: yExtentDomain - }, extent0 = this.__chart__ || extent1; - this.__chart__ = extent1; - if (d3_transitionInheritId) { - d3.select(this).transition().each("start.brush", function() { - xExtentDomain = extent0.i; - yExtentDomain = extent0.j; - xExtent = extent0.x; - yExtent = extent0.y; - event_({ - type: "brushstart" - }); - }).tween("brush:brush", function() { - var xi = d3_interpolateArray(xExtent, extent1.x), yi = d3_interpolateArray(yExtent, extent1.y); - xExtentDomain = yExtentDomain = null; - return function(t) { - xExtent = extent1.x = xi(t); - yExtent = extent1.y = yi(t); - event_({ - type: "brush", - mode: "resize" - }); - }; - }).each("end.brush", function() { - xExtentDomain = extent1.i; - yExtentDomain = extent1.j; - event_({ - type: "brush", - mode: "resize" - }); - event_({ - type: "brushend" - }); - }); - } else { - event_({ - type: "brushstart" - }); - event_({ - type: "brush", - mode: "resize" - }); - event_({ - type: "brushend" - }); - } - }); - }; - function redraw(g) { - g.selectAll(".resize").attr("transform", function(d) { - return "translate(" + xExtent[+/e$/.test(d)] + "," + yExtent[+/^s/.test(d)] + ")"; - }); - } - function redrawX(g) { - g.select(".extent").attr("x", xExtent[0]); - g.selectAll(".extent,.n>rect,.s>rect").attr("width", xExtent[1] - xExtent[0]); - } - function redrawY(g) { - g.select(".extent").attr("y", yExtent[0]); - g.selectAll(".extent,.e>rect,.w>rect").attr("height", yExtent[1] - yExtent[0]); - } - function brushstart() { - var target = this, eventTarget = d3.select(d3.event.target), event_ = event.of(target, arguments), g = d3.select(target), resizing = eventTarget.datum(), resizingX = !/^(n|s)$/.test(resizing) && x, resizingY = !/^(e|w)$/.test(resizing) && y, dragging = eventTarget.classed("extent"), dragRestore = d3_event_dragSuppress(), center, origin = d3.mouse(target), offset; - var w = d3.select(d3_window).on("keydown.brush", keydown).on("keyup.brush", keyup); - if (d3.event.changedTouches) { - w.on("touchmove.brush", brushmove).on("touchend.brush", brushend); - } else { - w.on("mousemove.brush", brushmove).on("mouseup.brush", brushend); - } - g.interrupt().selectAll("*").interrupt(); - if (dragging) { - origin[0] = xExtent[0] - origin[0]; - origin[1] = yExtent[0] - origin[1]; - } else if (resizing) { - var ex = +/w$/.test(resizing), ey = +/^n/.test(resizing); - offset = [ xExtent[1 - ex] - origin[0], yExtent[1 - ey] - origin[1] ]; - origin[0] = xExtent[ex]; - origin[1] = yExtent[ey]; - } else if (d3.event.altKey) center = origin.slice(); - g.style("pointer-events", "none").selectAll(".resize").style("display", null); - d3.select("body").style("cursor", eventTarget.style("cursor")); - event_({ - type: "brushstart" - }); - brushmove(); - function keydown() { - if (d3.event.keyCode == 32) { - if (!dragging) { - center = null; - origin[0] -= xExtent[1]; - origin[1] -= yExtent[1]; - dragging = 2; - } - d3_eventPreventDefault(); - } - } - function keyup() { - if (d3.event.keyCode == 32 && dragging == 2) { - origin[0] += xExtent[1]; - origin[1] += yExtent[1]; - dragging = 0; - d3_eventPreventDefault(); - } - } - function brushmove() { - var point = d3.mouse(target), moved = false; - if (offset) { - point[0] += offset[0]; - point[1] += offset[1]; - } - if (!dragging) { - if (d3.event.altKey) { - if (!center) center = [ (xExtent[0] + xExtent[1]) / 2, (yExtent[0] + yExtent[1]) / 2 ]; - origin[0] = xExtent[+(point[0] < center[0])]; - origin[1] = yExtent[+(point[1] < center[1])]; - } else center = null; - } - if (resizingX && move1(point, x, 0)) { - redrawX(g); - moved = true; - } - if (resizingY && move1(point, y, 1)) { - redrawY(g); - moved = true; - } - if (moved) { - redraw(g); - event_({ - type: "brush", - mode: dragging ? "move" : "resize" - }); - } - } - function move1(point, scale, i) { - var range = d3_scaleRange(scale), r0 = range[0], r1 = range[1], position = origin[i], extent = i ? yExtent : xExtent, size = extent[1] - extent[0], min, max; - if (dragging) { - r0 -= position; - r1 -= size + position; - } - min = (i ? yClamp : xClamp) ? Math.max(r0, Math.min(r1, point[i])) : point[i]; - if (dragging) { - max = (min += position) + size; - } else { - if (center) position = Math.max(r0, Math.min(r1, 2 * center[i] - min)); - if (position < min) { - max = min; - min = position; - } else { - max = position; - } - } - if (extent[0] != min || extent[1] != max) { - if (i) yExtentDomain = null; else xExtentDomain = null; - extent[0] = min; - extent[1] = max; - return true; - } - } - function brushend() { - brushmove(); - g.style("pointer-events", "all").selectAll(".resize").style("display", brush.empty() ? "none" : null); - d3.select("body").style("cursor", null); - w.on("mousemove.brush", null).on("mouseup.brush", null).on("touchmove.brush", null).on("touchend.brush", null).on("keydown.brush", null).on("keyup.brush", null); - dragRestore(); - event_({ - type: "brushend" - }); - } - } - brush.x = function(z) { - if (!arguments.length) return x; - x = z; - resizes = d3_svg_brushResizes[!x << 1 | !y]; - return brush; - }; - brush.y = function(z) { - if (!arguments.length) return y; - y = z; - resizes = d3_svg_brushResizes[!x << 1 | !y]; - return brush; - }; - brush.clamp = function(z) { - if (!arguments.length) return x && y ? [ xClamp, yClamp ] : x ? xClamp : y ? yClamp : null; - if (x && y) xClamp = !!z[0], yClamp = !!z[1]; else if (x) xClamp = !!z; else if (y) yClamp = !!z; - return brush; - }; - brush.extent = function(z) { - var x0, x1, y0, y1, t; - if (!arguments.length) { - if (x) { - if (xExtentDomain) { - x0 = xExtentDomain[0], x1 = xExtentDomain[1]; - } else { - x0 = xExtent[0], x1 = xExtent[1]; - if (x.invert) x0 = x.invert(x0), x1 = x.invert(x1); - if (x1 < x0) t = x0, x0 = x1, x1 = t; - } - } - if (y) { - if (yExtentDomain) { - y0 = yExtentDomain[0], y1 = yExtentDomain[1]; - } else { - y0 = yExtent[0], y1 = yExtent[1]; - if (y.invert) y0 = y.invert(y0), y1 = y.invert(y1); - if (y1 < y0) t = y0, y0 = y1, y1 = t; - } - } - return x && y ? [ [ x0, y0 ], [ x1, y1 ] ] : x ? [ x0, x1 ] : y && [ y0, y1 ]; - } - if (x) { - x0 = z[0], x1 = z[1]; - if (y) x0 = x0[0], x1 = x1[0]; - xExtentDomain = [ x0, x1 ]; - if (x.invert) x0 = x(x0), x1 = x(x1); - if (x1 < x0) t = x0, x0 = x1, x1 = t; - if (x0 != xExtent[0] || x1 != xExtent[1]) xExtent = [ x0, x1 ]; - } - if (y) { - y0 = z[0], y1 = z[1]; - if (x) y0 = y0[1], y1 = y1[1]; - yExtentDomain = [ y0, y1 ]; - if (y.invert) y0 = y(y0), y1 = y(y1); - if (y1 < y0) t = y0, y0 = y1, y1 = t; - if (y0 != yExtent[0] || y1 != yExtent[1]) yExtent = [ y0, y1 ]; - } - return brush; - }; - brush.clear = function() { - if (!brush.empty()) { - xExtent = [ 0, 0 ], yExtent = [ 0, 0 ]; - xExtentDomain = yExtentDomain = null; - } - return brush; - }; - brush.empty = function() { - return !!x && xExtent[0] == xExtent[1] || !!y && yExtent[0] == yExtent[1]; - }; - return d3.rebind(brush, event, "on"); - }; - var d3_svg_brushCursor = { - n: "ns-resize", - e: "ew-resize", - s: "ns-resize", - w: "ew-resize", - nw: "nwse-resize", - ne: "nesw-resize", - se: "nwse-resize", - sw: "nesw-resize" - }; - var d3_svg_brushResizes = [ [ "n", "e", "s", "w", "nw", "ne", "se", "sw" ], [ "e", "w" ], [ "n", "s" ], [] ]; - var d3_time_format = d3_time.format = d3_locale_enUS.timeFormat; - var d3_time_formatUtc = d3_time_format.utc; - var d3_time_formatIso = d3_time_formatUtc("%Y-%m-%dT%H:%M:%S.%LZ"); - d3_time_format.iso = Date.prototype.toISOString && +new Date("2000-01-01T00:00:00.000Z") ? d3_time_formatIsoNative : d3_time_formatIso; - function d3_time_formatIsoNative(date) { - return date.toISOString(); - } - d3_time_formatIsoNative.parse = function(string) { - var date = new Date(string); - return isNaN(date) ? null : date; - }; - d3_time_formatIsoNative.toString = d3_time_formatIso.toString; - d3_time.second = d3_time_interval(function(date) { - return new d3_date(Math.floor(date / 1e3) * 1e3); - }, function(date, offset) { - date.setTime(date.getTime() + Math.floor(offset) * 1e3); - }, function(date) { - return date.getSeconds(); - }); - d3_time.seconds = d3_time.second.range; - d3_time.seconds.utc = d3_time.second.utc.range; - d3_time.minute = d3_time_interval(function(date) { - return new d3_date(Math.floor(date / 6e4) * 6e4); - }, function(date, offset) { - date.setTime(date.getTime() + Math.floor(offset) * 6e4); - }, function(date) { - return date.getMinutes(); - }); - d3_time.minutes = d3_time.minute.range; - d3_time.minutes.utc = d3_time.minute.utc.range; - d3_time.hour = d3_time_interval(function(date) { - var timezone = date.getTimezoneOffset() / 60; - return new d3_date((Math.floor(date / 36e5 - timezone) + timezone) * 36e5); - }, function(date, offset) { - date.setTime(date.getTime() + Math.floor(offset) * 36e5); - }, function(date) { - return date.getHours(); - }); - d3_time.hours = d3_time.hour.range; - d3_time.hours.utc = d3_time.hour.utc.range; - d3_time.month = d3_time_interval(function(date) { - date = d3_time.day(date); - date.setDate(1); - return date; - }, function(date, offset) { - date.setMonth(date.getMonth() + offset); - }, function(date) { - return date.getMonth(); - }); - d3_time.months = d3_time.month.range; - d3_time.months.utc = d3_time.month.utc.range; - function d3_time_scale(linear, methods, format) { - function scale(x) { - return linear(x); - } - scale.invert = function(x) { - return d3_time_scaleDate(linear.invert(x)); - }; - scale.domain = function(x) { - if (!arguments.length) return linear.domain().map(d3_time_scaleDate); - linear.domain(x); - return scale; - }; - function tickMethod(extent, count) { - var span = extent[1] - extent[0], target = span / count, i = d3.bisect(d3_time_scaleSteps, target); - return i == d3_time_scaleSteps.length ? [ methods.year, d3_scale_linearTickRange(extent.map(function(d) { - return d / 31536e6; - }), count)[2] ] : !i ? [ d3_time_scaleMilliseconds, d3_scale_linearTickRange(extent, count)[2] ] : methods[target / d3_time_scaleSteps[i - 1] < d3_time_scaleSteps[i] / target ? i - 1 : i]; - } - scale.nice = function(interval, skip) { - var domain = scale.domain(), extent = d3_scaleExtent(domain), method = interval == null ? tickMethod(extent, 10) : typeof interval === "number" && tickMethod(extent, interval); - if (method) interval = method[0], skip = method[1]; - function skipped(date) { - return !isNaN(date) && !interval.range(date, d3_time_scaleDate(+date + 1), skip).length; - } - return scale.domain(d3_scale_nice(domain, skip > 1 ? { - floor: function(date) { - while (skipped(date = interval.floor(date))) date = d3_time_scaleDate(date - 1); - return date; - }, - ceil: function(date) { - while (skipped(date = interval.ceil(date))) date = d3_time_scaleDate(+date + 1); - return date; - } - } : interval)); - }; - scale.ticks = function(interval, skip) { - var extent = d3_scaleExtent(scale.domain()), method = interval == null ? tickMethod(extent, 10) : typeof interval === "number" ? tickMethod(extent, interval) : !interval.range && [ { - range: interval - }, skip ]; - if (method) interval = method[0], skip = method[1]; - return interval.range(extent[0], d3_time_scaleDate(+extent[1] + 1), skip < 1 ? 1 : skip); - }; - scale.tickFormat = function() { - return format; - }; - scale.copy = function() { - return d3_time_scale(linear.copy(), methods, format); - }; - return d3_scale_linearRebind(scale, linear); - } - function d3_time_scaleDate(t) { - return new Date(t); - } - var d3_time_scaleSteps = [ 1e3, 5e3, 15e3, 3e4, 6e4, 3e5, 9e5, 18e5, 36e5, 108e5, 216e5, 432e5, 864e5, 1728e5, 6048e5, 2592e6, 7776e6, 31536e6 ]; - var d3_time_scaleLocalMethods = [ [ d3_time.second, 1 ], [ d3_time.second, 5 ], [ d3_time.second, 15 ], [ d3_time.second, 30 ], [ d3_time.minute, 1 ], [ d3_time.minute, 5 ], [ d3_time.minute, 15 ], [ d3_time.minute, 30 ], [ d3_time.hour, 1 ], [ d3_time.hour, 3 ], [ d3_time.hour, 6 ], [ d3_time.hour, 12 ], [ d3_time.day, 1 ], [ d3_time.day, 2 ], [ d3_time.week, 1 ], [ d3_time.month, 1 ], [ d3_time.month, 3 ], [ d3_time.year, 1 ] ]; - var d3_time_scaleLocalFormat = d3_time_format.multi([ [ ".%L", function(d) { - return d.getMilliseconds(); - } ], [ ":%S", function(d) { - return d.getSeconds(); - } ], [ "%I:%M", function(d) { - return d.getMinutes(); - } ], [ "%I %p", function(d) { - return d.getHours(); - } ], [ "%a %d", function(d) { - return d.getDay() && d.getDate() != 1; - } ], [ "%b %d", function(d) { - return d.getDate() != 1; - } ], [ "%B", function(d) { - return d.getMonth(); - } ], [ "%Y", d3_true ] ]); - var d3_time_scaleMilliseconds = { - range: function(start, stop, step) { - return d3.range(Math.ceil(start / step) * step, +stop, step).map(d3_time_scaleDate); - }, - floor: d3_identity, - ceil: d3_identity - }; - d3_time_scaleLocalMethods.year = d3_time.year; - d3_time.scale = function() { - return d3_time_scale(d3.scale.linear(), d3_time_scaleLocalMethods, d3_time_scaleLocalFormat); - }; - var d3_time_scaleUtcMethods = d3_time_scaleLocalMethods.map(function(m) { - return [ m[0].utc, m[1] ]; - }); - var d3_time_scaleUtcFormat = d3_time_formatUtc.multi([ [ ".%L", function(d) { - return d.getUTCMilliseconds(); - } ], [ ":%S", function(d) { - return d.getUTCSeconds(); - } ], [ "%I:%M", function(d) { - return d.getUTCMinutes(); - } ], [ "%I %p", function(d) { - return d.getUTCHours(); - } ], [ "%a %d", function(d) { - return d.getUTCDay() && d.getUTCDate() != 1; - } ], [ "%b %d", function(d) { - return d.getUTCDate() != 1; - } ], [ "%B", function(d) { - return d.getUTCMonth(); - } ], [ "%Y", d3_true ] ]); - d3_time_scaleUtcMethods.year = d3_time.year.utc; - d3_time.scale.utc = function() { - return d3_time_scale(d3.scale.linear(), d3_time_scaleUtcMethods, d3_time_scaleUtcFormat); - }; - d3.text = d3_xhrType(function(request) { - return request.responseText; - }); - d3.json = function(url, callback) { - return d3_xhr(url, "application/json", d3_json, callback); - }; - function d3_json(request) { - return JSON.parse(request.responseText); - } - d3.html = function(url, callback) { - return d3_xhr(url, "text/html", d3_html, callback); - }; - function d3_html(request) { - var range = d3_document.createRange(); - range.selectNode(d3_document.body); - return range.createContextualFragment(request.responseText); - } - d3.xml = d3_xhrType(function(request) { - return request.responseXML; - }); - if (typeof define === "function" && define.amd) { - define(d3); - } else if (typeof module === "object" && module.exports) { - module.exports = d3; - } else { - this.d3 = d3; - } -}(); \ No newline at end of file diff --git a/vendor/assets/javascripts/d3.v3.min.js b/vendor/assets/javascripts/d3.v3.min.js deleted file mode 100644 index b8bffdc710..0000000000 --- a/vendor/assets/javascripts/d3.v3.min.js +++ /dev/null @@ -1,5 +0,0 @@ -d3=function(){function n(n){return null!=n&&!isNaN(n)}function t(n){return n.length}function e(n){for(var t=1;n*t%1;)t*=10;return t}function r(n,t){try{for(var e in t)Object.defineProperty(n.prototype,e,{value:t[e],enumerable:!1})}catch(r){n.prototype=t}}function u(){}function i(){}function o(n,t,e){return function(){var r=e.apply(t,arguments);return r===t?n:r}}function a(n,t){if(t in n)return t;t=t.charAt(0).toUpperCase()+t.substring(1);for(var e=0,r=la.length;r>e;++e){var u=la[e]+t;if(u in n)return u}}function c(){}function s(){}function l(n){function t(){for(var t,r=e,u=-1,i=r.length;++ue;e++)for(var u,i=n[e],o=0,a=i.length;a>o;o++)(u=i[o])&&t(u,o,e);return n}function N(n){return ha(n,xa),n}function L(n){var t,e;return function(r,u,i){var o,a=n[i].update,c=a.length;for(i!=e&&(e=i,t=0),u>=t&&(t=u+1);!(o=a[t])&&++t0&&(n=n.substring(0,a));var l=_a.get(n);return l&&(n=l,s=R),a?t?u:r:t?c:i}function z(n,t){return function(e){var r=$o.event;$o.event=e,t[0]=this.__data__;try{n.apply(this,t)}finally{$o.event=r}}}function R(n,t){var e=z(n,t);return function(n){var t=this,r=n.relatedTarget;r&&(r===t||8&r.compareDocumentPosition(t))||e.call(t,n)}}function D(){var n=".dragsuppress-"+ ++wa,t="click"+n,e=$o.select(Ko).on("touchmove"+n,f).on("dragstart"+n,f).on("selectstart"+n,f);if(ba){var r=Go.style,u=r[ba];r[ba]="none"}return function(i){function o(){e.on(t,null)}e.on(n,null),ba&&(r[ba]=u),i&&(e.on(t,function(){f(),o()},!0),setTimeout(o,0))}}function P(n,t){t.changedTouches&&(t=t.changedTouches[0]);var e=n.ownerSVGElement||n;if(e.createSVGPoint){var r=e.createSVGPoint();if(0>Sa&&(Ko.scrollX||Ko.scrollY)){e=$o.select("body").append("svg").style({position:"absolute",top:0,left:0,margin:0,padding:0,border:"none"},"important");var u=e[0][0].getScreenCTM();Sa=!(u.f||u.e),e.remove()}return Sa?(r.x=t.pageX,r.y=t.pageY):(r.x=t.clientX,r.y=t.clientY),r=r.matrixTransform(n.getScreenCTM().inverse()),[r.x,r.y]}var i=n.getBoundingClientRect();return[t.clientX-i.left-n.clientLeft,t.clientY-i.top-n.clientTop]}function U(n){return n>0?1:0>n?-1:0}function j(n){return n>1?0:-1>n?ka:Math.acos(n)}function H(n){return n>1?Aa:-1>n?-Aa:Math.asin(n)}function F(n){return((n=Math.exp(n))-1/n)/2}function O(n){return((n=Math.exp(n))+1/n)/2}function Y(n){return((n=Math.exp(2*n))-1)/(n+1)}function I(n){return(n=Math.sin(n/2))*n}function Z(){}function V(n,t,e){return new X(n,t,e)}function X(n,t,e){this.h=n,this.s=t,this.l=e}function $(n,t,e){function r(n){return n>360?n-=360:0>n&&(n+=360),60>n?i+(o-i)*n/60:180>n?o:240>n?i+(o-i)*(240-n)/60:i}function u(n){return Math.round(255*r(n))}var i,o;return n=isNaN(n)?0:(n%=360)<0?n+360:n,t=isNaN(t)?0:0>t?0:t>1?1:t,e=0>e?0:e>1?1:e,o=.5>=e?e*(1+t):e+t-e*t,i=2*e-o,ot(u(n+120),u(n),u(n-120))}function B(n,t,e){return new W(n,t,e)}function W(n,t,e){this.h=n,this.c=t,this.l=e}function J(n,t,e){return isNaN(n)&&(n=0),isNaN(t)&&(t=0),G(e,Math.cos(n*=La)*t,Math.sin(n)*t)}function G(n,t,e){return new K(n,t,e)}function K(n,t,e){this.l=n,this.a=t,this.b=e}function Q(n,t,e){var r=(n+16)/116,u=r+t/500,i=r-e/200;return u=tt(u)*Oa,r=tt(r)*Ya,i=tt(i)*Ia,ot(rt(3.2404542*u-1.5371385*r-.4985314*i),rt(-.969266*u+1.8760108*r+.041556*i),rt(.0556434*u-.2040259*r+1.0572252*i))}function nt(n,t,e){return n>0?B(Math.atan2(e,t)*Ta,Math.sqrt(t*t+e*e),n):B(0/0,0/0,n)}function tt(n){return n>.206893034?n*n*n:(n-4/29)/7.787037}function et(n){return n>.008856?Math.pow(n,1/3):7.787037*n+4/29}function rt(n){return Math.round(255*(.00304>=n?12.92*n:1.055*Math.pow(n,1/2.4)-.055))}function ut(n){return ot(n>>16,255&n>>8,255&n)}function it(n){return ut(n)+""}function ot(n,t,e){return new at(n,t,e)}function at(n,t,e){this.r=n,this.g=t,this.b=e}function ct(n){return 16>n?"0"+Math.max(0,n).toString(16):Math.min(255,n).toString(16)}function st(n,t,e){var r,u,i,o=0,a=0,c=0;if(r=/([a-z]+)\((.*)\)/i.exec(n))switch(u=r[2].split(","),r[1]){case"hsl":return e(parseFloat(u[0]),parseFloat(u[1])/100,parseFloat(u[2])/100);case"rgb":return t(gt(u[0]),gt(u[1]),gt(u[2]))}return(i=Xa.get(n))?t(i.r,i.g,i.b):(null!=n&&"#"===n.charAt(0)&&(4===n.length?(o=n.charAt(1),o+=o,a=n.charAt(2),a+=a,c=n.charAt(3),c+=c):7===n.length&&(o=n.substring(1,3),a=n.substring(3,5),c=n.substring(5,7)),o=parseInt(o,16),a=parseInt(a,16),c=parseInt(c,16)),t(o,a,c))}function lt(n,t,e){var r,u,i=Math.min(n/=255,t/=255,e/=255),o=Math.max(n,t,e),a=o-i,c=(o+i)/2;return a?(u=.5>c?a/(o+i):a/(2-o-i),r=n==o?(t-e)/a+(e>t?6:0):t==o?(e-n)/a+2:(n-t)/a+4,r*=60):(r=0/0,u=c>0&&1>c?0:r),V(r,u,c)}function ft(n,t,e){n=ht(n),t=ht(t),e=ht(e);var r=et((.4124564*n+.3575761*t+.1804375*e)/Oa),u=et((.2126729*n+.7151522*t+.072175*e)/Ya),i=et((.0193339*n+.119192*t+.9503041*e)/Ia);return G(116*u-16,500*(r-u),200*(u-i))}function ht(n){return(n/=255)<=.04045?n/12.92:Math.pow((n+.055)/1.055,2.4)}function gt(n){var t=parseFloat(n);return"%"===n.charAt(n.length-1)?Math.round(2.55*t):t}function pt(n){return"function"==typeof n?n:function(){return n}}function vt(n){return n}function dt(n){return function(t,e,r){return 2===arguments.length&&"function"==typeof e&&(r=e,e=null),mt(t,e,n,r)}}function mt(n,t,e,r){function u(){var n,t=c.status;if(!t&&c.responseText||t>=200&&300>t||304===t){try{n=e.call(i,c)}catch(r){return o.error.call(i,r),void 0}o.load.call(i,n)}else o.error.call(i,c)}var i={},o=$o.dispatch("beforesend","progress","load","error"),a={},c=new XMLHttpRequest,s=null;return!Ko.XDomainRequest||"withCredentials"in c||!/^(http(s)?:)?\/\//.test(n)||(c=new XDomainRequest),"onload"in c?c.onload=c.onerror=u:c.onreadystatechange=function(){c.readyState>3&&u()},c.onprogress=function(n){var t=$o.event;$o.event=n;try{o.progress.call(i,c)}finally{$o.event=t}},i.header=function(n,t){return n=(n+"").toLowerCase(),arguments.length<2?a[n]:(null==t?delete a[n]:a[n]=t+"",i)},i.mimeType=function(n){return arguments.length?(t=null==n?null:n+"",i):t},i.responseType=function(n){return arguments.length?(s=n,i):s},i.response=function(n){return e=n,i},["get","post"].forEach(function(n){i[n]=function(){return i.send.apply(i,[n].concat(Wo(arguments)))}}),i.send=function(e,r,u){if(2===arguments.length&&"function"==typeof r&&(u=r,r=null),c.open(e,n,!0),null==t||"accept"in a||(a.accept=t+",*/*"),c.setRequestHeader)for(var l in a)c.setRequestHeader(l,a[l]);return null!=t&&c.overrideMimeType&&c.overrideMimeType(t),null!=s&&(c.responseType=s),null!=u&&i.on("error",u).on("load",function(n){u(null,n)}),o.beforesend.call(i,c),c.send(null==r?null:r),i},i.abort=function(){return c.abort(),i},$o.rebind(i,o,"on"),null==r?i:i.get(yt(r))}function yt(n){return 1===n.length?function(t,e){n(null==t?e:null)}:n}function xt(){var n=Mt(),t=_t()-n;t>24?(isFinite(t)&&(clearTimeout(Ja),Ja=setTimeout(xt,t)),Wa=0):(Wa=1,Ka(xt))}function Mt(){var n=Date.now();for(Ga=$a;Ga;)n>=Ga.t&&(Ga.f=Ga.c(n-Ga.t)),Ga=Ga.n;return n}function _t(){for(var n,t=$a,e=1/0;t;)t.f?t=n?n.n=t.n:$a=t.n:(t.t8?function(n){return n/e}:function(n){return n*e},symbol:n}}function wt(n,t){return t-(n?Math.ceil(Math.log(n)/Math.LN10):1)}function St(n){return n+""}function kt(){}function Et(n,t,e){var r=e.s=n+t,u=r-n,i=r-u;e.t=n-i+(t-u)}function At(n,t){n&&lc.hasOwnProperty(n.type)&&lc[n.type](n,t)}function Ct(n,t,e){var r,u=-1,i=n.length-e;for(t.lineStart();++ua;++a)u.point((e=n[a])[0],e[1]);return u.lineEnd(),void 0}var c=new $t(e,n,null,!0),s=new $t(e,null,c,!1);c.o=s,i.push(c),o.push(s),c=new $t(r,n,null,!1),s=new $t(r,null,c,!0),c.o=s,i.push(c),o.push(s)}}),o.sort(t),Xt(i),Xt(o),i.length){for(var a=0,c=e,s=o.length;s>a;++a)o[a].e=c=!c;for(var l,f,h=i[0];;){for(var g=h,p=!0;g.v;)if((g=g.n)===h)return;l=g.z,u.lineStart();do{if(g.v=g.o.v=!0,g.e){if(p)for(var a=0,s=l.length;s>a;++a)u.point((f=l[a])[0],f[1]);else r(g.x,g.n.x,1,u);g=g.n}else{if(p){l=g.p.z;for(var a=l.length-1;a>=0;--a)u.point((f=l[a])[0],f[1])}else r(g.x,g.p.x,-1,u);g=g.p}g=g.o,l=g.z,p=!p}while(!g.v);u.lineEnd()}}}function Xt(n){if(t=n.length){for(var t,e,r=0,u=n[0];++r1&&2&t&&e.push(e.pop().concat(e.shift())),g.push(e.filter(Wt))}}var g,p,v,d=t(i),m=u.invert(r[0],r[1]),y={point:o,lineStart:c,lineEnd:s,polygonStart:function(){y.point=l,y.lineStart=f,y.lineEnd=h,g=[],p=[],i.polygonStart()},polygonEnd:function(){y.point=o,y.lineStart=c,y.lineEnd=s,g=$o.merge(g);var n=Kt(m,p);g.length?Vt(g,Gt,n,e,i):n&&(i.lineStart(),e(null,null,1,i),i.lineEnd()),i.polygonEnd(),g=p=null},sphere:function(){i.polygonStart(),i.lineStart(),e(null,null,1,i),i.lineEnd(),i.polygonEnd()}},x=Jt(),M=t(x);return y}}function Wt(n){return n.length>1}function Jt(){var n,t=[];return{lineStart:function(){t.push(n=[])},point:function(t,e){n.push([t,e])},lineEnd:c,buffer:function(){var e=t;return t=[],n=null,e},rejoin:function(){t.length>1&&t.push(t.pop().concat(t.shift()))}}}function Gt(n,t){return((n=n.x)[0]<0?n[1]-Aa-Ca:Aa-n[1])-((t=t.x)[0]<0?t[1]-Aa-Ca:Aa-t[1])}function Kt(n,t){var e=n[0],r=n[1],u=[Math.sin(e),-Math.cos(e),0],i=0,o=0;hc.reset();for(var a=0,c=t.length;c>a;++a){var s=t[a],l=s.length;if(l)for(var f=s[0],h=f[0],g=f[1]/2+ka/4,p=Math.sin(g),v=Math.cos(g),d=1;;){d===l&&(d=0),n=s[d];var m=n[0],y=n[1]/2+ka/4,x=Math.sin(y),M=Math.cos(y),_=m-h,b=aa(_)>ka,w=p*x;if(hc.add(Math.atan2(w*Math.sin(_),v*M+w*Math.cos(_))),i+=b?_+(_>=0?Ea:-Ea):_,b^h>=e^m>=e){var S=zt(Tt(f),Tt(n));Pt(S);var k=zt(u,S);Pt(k);var E=(b^_>=0?-1:1)*H(k[2]);(r>E||r===E&&(S[0]||S[1]))&&(o+=b^_>=0?1:-1)}if(!d++)break;h=m,p=x,v=M,f=n}}return(-Ca>i||Ca>i&&0>hc)^1&o}function Qt(n){var t,e=0/0,r=0/0,u=0/0;return{lineStart:function(){n.lineStart(),t=1},point:function(i,o){var a=i>0?ka:-ka,c=aa(i-e);aa(c-ka)0?Aa:-Aa),n.point(u,r),n.lineEnd(),n.lineStart(),n.point(a,r),n.point(i,r),t=0):u!==a&&c>=ka&&(aa(e-u)Ca?Math.atan((Math.sin(t)*(i=Math.cos(r))*Math.sin(e)-Math.sin(r)*(u=Math.cos(t))*Math.sin(n))/(u*i*o)):(t+r)/2}function te(n,t,e,r){var u;if(null==n)u=e*Aa,r.point(-ka,u),r.point(0,u),r.point(ka,u),r.point(ka,0),r.point(ka,-u),r.point(0,-u),r.point(-ka,-u),r.point(-ka,0),r.point(-ka,u);else if(aa(n[0]-t[0])>Ca){var i=n[0]i}function e(n){var e,i,c,s,l;return{lineStart:function(){s=c=!1,l=1},point:function(f,h){var g,p=[f,h],v=t(f,h),d=o?v?0:u(f,h):v?u(f+(0>f?ka:-ka),h):0;if(!e&&(s=c=v)&&n.lineStart(),v!==c&&(g=r(e,p),(jt(e,g)||jt(p,g))&&(p[0]+=Ca,p[1]+=Ca,v=t(p[0],p[1]))),v!==c)l=0,v?(n.lineStart(),g=r(p,e),n.point(g[0],g[1])):(g=r(e,p),n.point(g[0],g[1]),n.lineEnd()),e=g;else if(a&&e&&o^v){var m;d&i||!(m=r(p,e,!0))||(l=0,o?(n.lineStart(),n.point(m[0][0],m[0][1]),n.point(m[1][0],m[1][1]),n.lineEnd()):(n.point(m[1][0],m[1][1]),n.lineEnd(),n.lineStart(),n.point(m[0][0],m[0][1])))}!v||e&&jt(e,p)||n.point(p[0],p[1]),e=p,c=v,i=d},lineEnd:function(){c&&n.lineEnd(),e=null},clean:function(){return l|(s&&c)<<1}}}function r(n,t,e){var r=Tt(n),u=Tt(t),o=[1,0,0],a=zt(r,u),c=qt(a,a),s=a[0],l=c-s*s;if(!l)return!e&&n;var f=i*c/l,h=-i*s/l,g=zt(o,a),p=Dt(o,f),v=Dt(a,h);Rt(p,v);var d=g,m=qt(p,d),y=qt(d,d),x=m*m-y*(qt(p,p)-1);if(!(0>x)){var M=Math.sqrt(x),_=Dt(d,(-m-M)/y);if(Rt(_,p),_=Ut(_),!e)return _;var b,w=n[0],S=t[0],k=n[1],E=t[1];w>S&&(b=w,w=S,S=b);var A=S-w,C=aa(A-ka)A;if(!C&&k>E&&(b=k,k=E,E=b),N?C?k+E>0^_[1]<(aa(_[0]-w)ka^(w<=_[0]&&_[0]<=S)){var L=Dt(d,(-m+M)/y);return Rt(L,p),[_,Ut(L)]}}}function u(t,e){var r=o?n:ka-n,u=0;return-r>t?u|=1:t>r&&(u|=2),-r>e?u|=4:e>r&&(u|=8),u}var i=Math.cos(n),o=i>0,a=aa(i)>Ca,c=Le(n,6*La);return Bt(t,e,c,o?[0,-n]:[-ka,n-ka])}function re(n,t,e,r){return function(u){var i,o=u.a,a=u.b,c=o.x,s=o.y,l=a.x,f=a.y,h=0,g=1,p=l-c,v=f-s;if(i=n-c,p||!(i>0)){if(i/=p,0>p){if(h>i)return;g>i&&(g=i)}else if(p>0){if(i>g)return;i>h&&(h=i)}if(i=e-c,p||!(0>i)){if(i/=p,0>p){if(i>g)return;i>h&&(h=i)}else if(p>0){if(h>i)return;g>i&&(g=i)}if(i=t-s,v||!(i>0)){if(i/=v,0>v){if(h>i)return;g>i&&(g=i)}else if(v>0){if(i>g)return;i>h&&(h=i)}if(i=r-s,v||!(0>i)){if(i/=v,0>v){if(i>g)return;i>h&&(h=i)}else if(v>0){if(h>i)return;g>i&&(g=i)}return h>0&&(u.a={x:c+h*p,y:s+h*v}),1>g&&(u.b={x:c+g*p,y:s+g*v}),u}}}}}}function ue(n,t,e,r){function u(r,u){return aa(r[0]-n)0?0:3:aa(r[0]-e)0?2:1:aa(r[1]-t)0?1:0:u>0?3:2}function i(n,t){return o(n.x,t.x)}function o(n,t){var e=u(n,1),r=u(t,1);return e!==r?e-r:0===e?t[1]-n[1]:1===e?n[0]-t[0]:2===e?n[1]-t[1]:t[0]-n[0]}return function(a){function c(n){for(var t=0,e=m.length,r=n[1],u=0;e>u;++u)for(var i,o=1,a=m[u],c=a.length,l=a[0];c>o;++o)i=a[o],l[1]<=r?i[1]>r&&s(l,i,n)>0&&++t:i[1]<=r&&s(l,i,n)<0&&--t,l=i;return 0!==t}function s(n,t,e){return(t[0]-n[0])*(e[1]-n[1])-(e[0]-n[0])*(t[1]-n[1])}function l(i,a,c,s){var l=0,f=0;if(null==i||(l=u(i,c))!==(f=u(a,c))||o(i,a)<0^c>0){do s.point(0===l||3===l?n:e,l>1?r:t);while((l=(l+c+4)%4)!==f)}else s.point(a[0],a[1])}function f(u,i){return u>=n&&e>=u&&i>=t&&r>=i}function h(n,t){f(n,t)&&a.point(n,t)}function g(){L.point=v,m&&m.push(y=[]),k=!0,S=!1,b=w=0/0}function p(){d&&(v(x,M),_&&S&&C.rejoin(),d.push(C.buffer())),L.point=h,S&&a.lineEnd()}function v(n,t){n=Math.max(-Ac,Math.min(Ac,n)),t=Math.max(-Ac,Math.min(Ac,t));var e=f(n,t);if(m&&y.push([n,t]),k)x=n,M=t,_=e,k=!1,e&&(a.lineStart(),a.point(n,t));else if(e&&S)a.point(n,t);else{var r={a:{x:b,y:w},b:{x:n,y:t}};N(r)?(S||(a.lineStart(),a.point(r.a.x,r.a.y)),a.point(r.b.x,r.b.y),e||a.lineEnd(),E=!1):e&&(a.lineStart(),a.point(n,t),E=!1)}b=n,w=t,S=e}var d,m,y,x,M,_,b,w,S,k,E,A=a,C=Jt(),N=re(n,t,e,r),L={point:h,lineStart:g,lineEnd:p,polygonStart:function(){a=C,d=[],m=[],E=!0},polygonEnd:function(){a=A,d=$o.merge(d);var t=c([n,r]),e=E&&t,u=d.length;(e||u)&&(a.polygonStart(),e&&(a.lineStart(),l(null,null,1,a),a.lineEnd()),u&&Vt(d,i,t,l,a),a.polygonEnd()),d=m=y=null}};return L}}function ie(n,t){function e(e,r){return e=n(e,r),t(e[0],e[1])}return n.invert&&t.invert&&(e.invert=function(e,r){return e=t.invert(e,r),e&&n.invert(e[0],e[1])}),e}function oe(n){var t=0,e=ka/3,r=be(n),u=r(t,e);return u.parallels=function(n){return arguments.length?r(t=n[0]*ka/180,e=n[1]*ka/180):[180*(t/ka),180*(e/ka)]},u}function ae(n,t){function e(n,t){var e=Math.sqrt(i-2*u*Math.sin(t))/u;return[e*Math.sin(n*=u),o-e*Math.cos(n)]}var r=Math.sin(n),u=(r+Math.sin(t))/2,i=1+r*(2*u-r),o=Math.sqrt(i)/u;return e.invert=function(n,t){var e=o-t;return[Math.atan2(n,e)/u,H((i-(n*n+e*e)*u*u)/(2*u))]},e}function ce(){function n(n,t){Nc+=u*n-r*t,r=n,u=t}var t,e,r,u;Rc.point=function(i,o){Rc.point=n,t=r=i,e=u=o},Rc.lineEnd=function(){n(t,e)}}function se(n,t){Lc>n&&(Lc=n),n>qc&&(qc=n),Tc>t&&(Tc=t),t>zc&&(zc=t)}function le(){function n(n,t){o.push("M",n,",",t,i)}function t(n,t){o.push("M",n,",",t),a.point=e}function e(n,t){o.push("L",n,",",t)}function r(){a.point=n}function u(){o.push("Z")}var i=fe(4.5),o=[],a={point:n,lineStart:function(){a.point=t},lineEnd:r,polygonStart:function(){a.lineEnd=u},polygonEnd:function(){a.lineEnd=r,a.point=n},pointRadius:function(n){return i=fe(n),a},result:function(){if(o.length){var n=o.join("");return o=[],n}}};return a}function fe(n){return"m0,"+n+"a"+n+","+n+" 0 1,1 0,"+-2*n+"a"+n+","+n+" 0 1,1 0,"+2*n+"z"}function he(n,t){dc+=n,mc+=t,++yc}function ge(){function n(n,r){var u=n-t,i=r-e,o=Math.sqrt(u*u+i*i);xc+=o*(t+n)/2,Mc+=o*(e+r)/2,_c+=o,he(t=n,e=r)}var t,e;Pc.point=function(r,u){Pc.point=n,he(t=r,e=u)}}function pe(){Pc.point=he}function ve(){function n(n,t){var e=n-r,i=t-u,o=Math.sqrt(e*e+i*i);xc+=o*(r+n)/2,Mc+=o*(u+t)/2,_c+=o,o=u*n-r*t,bc+=o*(r+n),wc+=o*(u+t),Sc+=3*o,he(r=n,u=t)}var t,e,r,u;Pc.point=function(i,o){Pc.point=n,he(t=r=i,e=u=o)},Pc.lineEnd=function(){n(t,e)}}function de(n){function t(t,e){n.moveTo(t,e),n.arc(t,e,o,0,Ea)}function e(t,e){n.moveTo(t,e),a.point=r}function r(t,e){n.lineTo(t,e)}function u(){a.point=t}function i(){n.closePath()}var o=4.5,a={point:t,lineStart:function(){a.point=e},lineEnd:u,polygonStart:function(){a.lineEnd=i},polygonEnd:function(){a.lineEnd=u,a.point=t},pointRadius:function(n){return o=n,a},result:c};return a}function me(n){function t(n){return(a?r:e)(n)}function e(t){return Me(t,function(e,r){e=n(e,r),t.point(e[0],e[1])})}function r(t){function e(e,r){e=n(e,r),t.point(e[0],e[1])}function r(){x=0/0,S.point=i,t.lineStart()}function i(e,r){var i=Tt([e,r]),o=n(e,r);u(x,M,y,_,b,w,x=o[0],M=o[1],y=e,_=i[0],b=i[1],w=i[2],a,t),t.point(x,M)}function o(){S.point=e,t.lineEnd()}function c(){r(),S.point=s,S.lineEnd=l}function s(n,t){i(f=n,h=t),g=x,p=M,v=_,d=b,m=w,S.point=i}function l(){u(x,M,y,_,b,w,g,p,f,v,d,m,a,t),S.lineEnd=o,o()}var f,h,g,p,v,d,m,y,x,M,_,b,w,S={point:e,lineStart:r,lineEnd:o,polygonStart:function(){t.polygonStart(),S.lineStart=c},polygonEnd:function(){t.polygonEnd(),S.lineStart=r}};return S}function u(t,e,r,a,c,s,l,f,h,g,p,v,d,m){var y=l-t,x=f-e,M=y*y+x*x;if(M>4*i&&d--){var _=a+g,b=c+p,w=s+v,S=Math.sqrt(_*_+b*b+w*w),k=Math.asin(w/=S),E=aa(aa(w)-1)i||aa((y*L+x*T)/M-.5)>.3||o>a*g+c*p+s*v)&&(u(t,e,r,a,c,s,C,N,E,_/=S,b/=S,w,d,m),m.point(C,N),u(C,N,E,_,b,w,l,f,h,g,p,v,d,m))}}var i=.5,o=Math.cos(30*La),a=16;return t.precision=function(n){return arguments.length?(a=(i=n*n)>0&&16,t):Math.sqrt(i)},t}function ye(n){var t=me(function(t,e){return n([t*Ta,e*Ta])});return function(n){return we(t(n))}}function xe(n){this.stream=n}function Me(n,t){return{point:t,sphere:function(){n.sphere()},lineStart:function(){n.lineStart()},lineEnd:function(){n.lineEnd()},polygonStart:function(){n.polygonStart()},polygonEnd:function(){n.polygonEnd()}}}function _e(n){return be(function(){return n})()}function be(n){function t(n){return n=a(n[0]*La,n[1]*La),[n[0]*h+c,s-n[1]*h]}function e(n){return n=a.invert((n[0]-c)/h,(s-n[1])/h),n&&[n[0]*Ta,n[1]*Ta]}function r(){a=ie(o=Ee(m,y,x),i);var n=i(v,d);return c=g-n[0]*h,s=p+n[1]*h,u()}function u(){return l&&(l.valid=!1,l=null),t}var i,o,a,c,s,l,f=me(function(n,t){return n=i(n,t),[n[0]*h+c,s-n[1]*h]}),h=150,g=480,p=250,v=0,d=0,m=0,y=0,x=0,M=Ec,_=vt,b=null,w=null;return t.stream=function(n){return l&&(l.valid=!1),l=we(M(o,f(_(n)))),l.valid=!0,l},t.clipAngle=function(n){return arguments.length?(M=null==n?(b=n,Ec):ee((b=+n)*La),u()):b},t.clipExtent=function(n){return arguments.length?(w=n,_=n?ue(n[0][0],n[0][1],n[1][0],n[1][1]):vt,u()):w},t.scale=function(n){return arguments.length?(h=+n,r()):h},t.translate=function(n){return arguments.length?(g=+n[0],p=+n[1],r()):[g,p]},t.center=function(n){return arguments.length?(v=n[0]%360*La,d=n[1]%360*La,r()):[v*Ta,d*Ta]},t.rotate=function(n){return arguments.length?(m=n[0]%360*La,y=n[1]%360*La,x=n.length>2?n[2]%360*La:0,r()):[m*Ta,y*Ta,x*Ta]},$o.rebind(t,f,"precision"),function(){return i=n.apply(this,arguments),t.invert=i.invert&&e,r()}}function we(n){return Me(n,function(t,e){n.point(t*La,e*La)})}function Se(n,t){return[n,t]}function ke(n,t){return[n>ka?n-Ea:-ka>n?n+Ea:n,t]}function Ee(n,t,e){return n?t||e?ie(Ce(n),Ne(t,e)):Ce(n):t||e?Ne(t,e):ke}function Ae(n){return function(t,e){return t+=n,[t>ka?t-Ea:-ka>t?t+Ea:t,e]}}function Ce(n){var t=Ae(n);return t.invert=Ae(-n),t}function Ne(n,t){function e(n,t){var e=Math.cos(t),a=Math.cos(n)*e,c=Math.sin(n)*e,s=Math.sin(t),l=s*r+a*u;return[Math.atan2(c*i-l*o,a*r-s*u),H(l*i+c*o)]}var r=Math.cos(n),u=Math.sin(n),i=Math.cos(t),o=Math.sin(t);return e.invert=function(n,t){var e=Math.cos(t),a=Math.cos(n)*e,c=Math.sin(n)*e,s=Math.sin(t),l=s*i-c*o;return[Math.atan2(c*i+s*o,a*r+l*u),H(l*r-a*u)]},e}function Le(n,t){var e=Math.cos(n),r=Math.sin(n);return function(u,i,o,a){var c=o*t;null!=u?(u=Te(e,u),i=Te(e,i),(o>0?i>u:u>i)&&(u+=o*Ea)):(u=n+o*Ea,i=n-.5*c);for(var s,l=u;o>0?l>i:i>l;l-=c)a.point((s=Ut([e,-r*Math.cos(l),-r*Math.sin(l)]))[0],s[1])}}function Te(n,t){var e=Tt(t);e[0]-=n,Pt(e);var r=j(-e[1]);return((-e[2]<0?-r:r)+2*Math.PI-Ca)%(2*Math.PI)}function qe(n,t,e){var r=$o.range(n,t-Ca,e).concat(t);return function(n){return r.map(function(t){return[n,t]})}}function ze(n,t,e){var r=$o.range(n,t-Ca,e).concat(t);return function(n){return r.map(function(t){return[t,n]})}}function Re(n){return n.source}function De(n){return n.target}function Pe(n,t,e,r){var u=Math.cos(t),i=Math.sin(t),o=Math.cos(r),a=Math.sin(r),c=u*Math.cos(n),s=u*Math.sin(n),l=o*Math.cos(e),f=o*Math.sin(e),h=2*Math.asin(Math.sqrt(I(r-t)+u*o*I(e-n))),g=1/Math.sin(h),p=h?function(n){var t=Math.sin(n*=h)*g,e=Math.sin(h-n)*g,r=e*c+t*l,u=e*s+t*f,o=e*i+t*a;return[Math.atan2(u,r)*Ta,Math.atan2(o,Math.sqrt(r*r+u*u))*Ta]}:function(){return[n*Ta,t*Ta]};return p.distance=h,p}function Ue(){function n(n,u){var i=Math.sin(u*=La),o=Math.cos(u),a=aa((n*=La)-t),c=Math.cos(a);Uc+=Math.atan2(Math.sqrt((a=o*Math.sin(a))*a+(a=r*i-e*o*c)*a),e*i+r*o*c),t=n,e=i,r=o}var t,e,r;jc.point=function(u,i){t=u*La,e=Math.sin(i*=La),r=Math.cos(i),jc.point=n},jc.lineEnd=function(){jc.point=jc.lineEnd=c}}function je(n,t){function e(t,e){var r=Math.cos(t),u=Math.cos(e),i=n(r*u);return[i*u*Math.sin(t),i*Math.sin(e)]}return e.invert=function(n,e){var r=Math.sqrt(n*n+e*e),u=t(r),i=Math.sin(u),o=Math.cos(u);return[Math.atan2(n*i,r*o),Math.asin(r&&e*i/r)]},e}function He(n,t){function e(n,t){var e=aa(aa(t)-Aa)0}function $e(n,t,e){return(e[0]-t[0])*(n[1]-t[1])<(e[1]-t[1])*(n[0]-t[0])}function Be(n,t,e,r){var u=n[0],i=e[0],o=t[0]-u,a=r[0]-i,c=n[1],s=e[1],l=t[1]-c,f=r[1]-s,h=(a*(c-s)-f*(u-i))/(f*o-a*l);return[u+h*o,c+h*l]}function We(n){var t=n[0],e=n[n.length-1];return!(t[0]-e[0]||t[1]-e[1])}function Je(){mr(this),this.edge=this.site=this.circle=null}function Ge(n){var t=Jc.pop()||new Je;return t.site=n,t}function Ke(n){cr(n),$c.remove(n),Jc.push(n),mr(n)}function Qe(n){var t=n.circle,e=t.x,r=t.cy,u={x:e,y:r},i=n.P,o=n.N,a=[n];Ke(n);for(var c=i;c.circle&&aa(e-c.circle.x)l;++l)s=a[l],c=a[l-1],pr(s.edge,c.site,s.site,u);c=a[0],s=a[f-1],s.edge=hr(c.site,s.site,null,u),ar(c),ar(s)}function nr(n){for(var t,e,r,u,i=n.x,o=n.y,a=$c._;a;)if(r=tr(a,o)-i,r>Ca)a=a.L;else{if(u=i-er(a,o),!(u>Ca)){r>-Ca?(t=a.P,e=a):u>-Ca?(t=a,e=a.N):t=e=a;break}if(!a.R){t=a;break}a=a.R}var c=Ge(n);if($c.insert(t,c),t||e){if(t===e)return cr(t),e=Ge(t.site),$c.insert(c,e),c.edge=e.edge=hr(t.site,c.site),ar(t),ar(e),void 0;if(!e)return c.edge=hr(t.site,c.site),void 0;cr(t),cr(e);var s=t.site,l=s.x,f=s.y,h=n.x-l,g=n.y-f,p=e.site,v=p.x-l,d=p.y-f,m=2*(h*d-g*v),y=h*h+g*g,x=v*v+d*d,M={x:(d*y-g*x)/m+l,y:(h*x-v*y)/m+f};pr(e.edge,s,p,M),c.edge=hr(s,n,null,M),e.edge=hr(n,p,null,M),ar(t),ar(e)}}function tr(n,t){var e=n.site,r=e.x,u=e.y,i=u-t;if(!i)return r;var o=n.P;if(!o)return-1/0;e=o.site;var a=e.x,c=e.y,s=c-t;if(!s)return a;var l=a-r,f=1/i-1/s,h=l/s;return f?(-h+Math.sqrt(h*h-2*f*(l*l/(-2*s)-c+s/2+u-i/2)))/f+r:(r+a)/2}function er(n,t){var e=n.N;if(e)return tr(e,t);var r=n.site;return r.y===t?r.x:1/0}function rr(n){this.site=n,this.edges=[]}function ur(n){for(var t,e,r,u,i,o,a,c,s,l,f=n[0][0],h=n[1][0],g=n[0][1],p=n[1][1],v=Xc,d=v.length;d--;)if(i=v[d],i&&i.prepare())for(a=i.edges,c=a.length,o=0;c>o;)l=a[o].end(),r=l.x,u=l.y,s=a[++o%c].start(),t=s.x,e=s.y,(aa(r-t)>Ca||aa(u-e)>Ca)&&(a.splice(o,0,new vr(gr(i.site,l,aa(r-f)Ca?{x:f,y:aa(t-f)Ca?{x:aa(e-p)Ca?{x:h,y:aa(t-h)Ca?{x:aa(e-g)=-Na)){var g=c*c+s*s,p=l*l+f*f,v=(f*g-s*p)/h,d=(c*p-l*g)/h,f=d+a,m=Gc.pop()||new or;m.arc=n,m.site=u,m.x=v+o,m.y=f+Math.sqrt(v*v+d*d),m.cy=f,n.circle=m;for(var y=null,x=Wc._;x;)if(m.yd||d>=a)return;if(h>p){if(i){if(i.y>=s)return}else i={x:d,y:c};e={x:d,y:s}}else{if(i){if(i.yr||r>1)if(h>p){if(i){if(i.y>=s)return}else i={x:(c-u)/r,y:c};e={x:(s-u)/r,y:s}}else{if(i){if(i.yg){if(i){if(i.x>=a)return}else i={x:o,y:r*o+u};e={x:a,y:r*a+u}}else{if(i){if(i.xr;++r)if(o=l[r],o.x==e[0]){if(o.i)if(null==s[o.i+1])for(s[o.i-1]+=o.x,s.splice(o.i,1),u=r+1;i>u;++u)l[u].i--;else for(s[o.i-1]+=o.x+s[o.i+1],s.splice(o.i,2),u=r+1;i>u;++u)l[u].i-=2;else if(null==s[o.i+1])s[o.i]=o.x;else for(s[o.i]=o.x+s[o.i+1],s.splice(o.i+1,1),u=r+1;i>u;++u)l[u].i--;l.splice(r,1),i--,r--}else o.x=Lr(parseFloat(e[0]),parseFloat(o.x));for(;i>r;)o=l.pop(),null==s[o.i+1]?s[o.i]=o.x:(s[o.i]=o.x+s[o.i+1],s.splice(o.i+1,1)),i--;return 1===s.length?null==s[0]?(o=l[0].x,function(n){return o(n)+""}):function(){return t}:function(n){for(r=0;i>r;++r)s[(o=l[r]).i]=o.x(n);return s.join("")}}function qr(n,t){for(var e,r=$o.interpolators.length;--r>=0&&!(e=$o.interpolators[r](n,t)););return e}function zr(n,t){var e,r=[],u=[],i=n.length,o=t.length,a=Math.min(n.length,t.length);for(e=0;a>e;++e)r.push(qr(n[e],t[e]));for(;i>e;++e)u[e]=n[e];for(;o>e;++e)u[e]=t[e];return function(n){for(e=0;a>e;++e)u[e]=r[e](n);return u}}function Rr(n){return function(t){return 0>=t?0:t>=1?1:n(t)}}function Dr(n){return function(t){return 1-n(1-t)}}function Pr(n){return function(t){return.5*(.5>t?n(2*t):2-n(2-2*t))}}function Ur(n){return n*n}function jr(n){return n*n*n}function Hr(n){if(0>=n)return 0;if(n>=1)return 1;var t=n*n,e=t*n;return 4*(.5>n?e:3*(n-t)+e-.75)}function Fr(n){return function(t){return Math.pow(t,n)}}function Or(n){return 1-Math.cos(n*Aa)}function Yr(n){return Math.pow(2,10*(n-1))}function Ir(n){return 1-Math.sqrt(1-n*n)}function Zr(n,t){var e;return arguments.length<2&&(t=.45),arguments.length?e=t/Ea*Math.asin(1/n):(n=1,e=t/4),function(r){return 1+n*Math.pow(2,-10*r)*Math.sin((r-e)*Ea/t)}}function Vr(n){return n||(n=1.70158),function(t){return t*t*((n+1)*t-n)}}function Xr(n){return 1/2.75>n?7.5625*n*n:2/2.75>n?7.5625*(n-=1.5/2.75)*n+.75:2.5/2.75>n?7.5625*(n-=2.25/2.75)*n+.9375:7.5625*(n-=2.625/2.75)*n+.984375}function $r(n,t){n=$o.hcl(n),t=$o.hcl(t);var e=n.h,r=n.c,u=n.l,i=t.h-e,o=t.c-r,a=t.l-u;return isNaN(o)&&(o=0,r=isNaN(r)?t.c:r),isNaN(i)?(i=0,e=isNaN(e)?t.h:e):i>180?i-=360:-180>i&&(i+=360),function(n){return J(e+i*n,r+o*n,u+a*n)+""}}function Br(n,t){n=$o.hsl(n),t=$o.hsl(t);var e=n.h,r=n.s,u=n.l,i=t.h-e,o=t.s-r,a=t.l-u;return isNaN(o)&&(o=0,r=isNaN(r)?t.s:r),isNaN(i)?(i=0,e=isNaN(e)?t.h:e):i>180?i-=360:-180>i&&(i+=360),function(n){return $(e+i*n,r+o*n,u+a*n)+""}}function Wr(n,t){n=$o.lab(n),t=$o.lab(t);var e=n.l,r=n.a,u=n.b,i=t.l-e,o=t.a-r,a=t.b-u;return function(n){return Q(e+i*n,r+o*n,u+a*n)+""}}function Jr(n,t){return t-=n,function(e){return Math.round(n+t*e)}}function Gr(n){var t=[n.a,n.b],e=[n.c,n.d],r=Qr(t),u=Kr(t,e),i=Qr(nu(e,t,-u))||0;t[0]*e[1]180?l+=360:l-s>180&&(s+=360),u.push({i:r.push(r.pop()+"rotate(",null,")")-2,x:Lr(s,l)})):l&&r.push(r.pop()+"rotate("+l+")"),f!=h?u.push({i:r.push(r.pop()+"skewX(",null,")")-2,x:Lr(f,h)}):h&&r.push(r.pop()+"skewX("+h+")"),g[0]!=p[0]||g[1]!=p[1]?(e=r.push(r.pop()+"scale(",null,",",null,")"),u.push({i:e-4,x:Lr(g[0],p[0])},{i:e-2,x:Lr(g[1],p[1])})):(1!=p[0]||1!=p[1])&&r.push(r.pop()+"scale("+p+")"),e=u.length,function(n){for(var t,i=-1;++ie;++e)(t=n[e][1])>u&&(r=e,u=t);return r}function wu(n){return n.reduce(Su,0)}function Su(n,t){return n+t[1]}function ku(n,t){return Eu(n,Math.ceil(Math.log(t.length)/Math.LN2+1))}function Eu(n,t){for(var e=-1,r=+n[0],u=(n[1]-r)/t,i=[];++e<=t;)i[e]=u*e+r;return i}function Au(n){return[$o.min(n),$o.max(n)]}function Cu(n,t){return n.parent==t.parent?1:2}function Nu(n){var t=n.children;return t&&t.length?t[0]:n._tree.thread}function Lu(n){var t,e=n.children;return e&&(t=e.length)?e[t-1]:n._tree.thread}function Tu(n,t){var e=n.children;if(e&&(u=e.length))for(var r,u,i=-1;++i0&&(n=r);return n}function qu(n,t){return n.x-t.x}function zu(n,t){return t.x-n.x}function Ru(n,t){return n.depth-t.depth}function Du(n,t){function e(n,r){var u=n.children;if(u&&(o=u.length))for(var i,o,a=null,c=-1;++c=0;)t=u[i]._tree,t.prelim+=e,t.mod+=e,e+=t.shift+(r+=t.change)}function Uu(n,t,e){n=n._tree,t=t._tree;var r=e/(t.number-n.number);n.change+=r,t.change-=r,t.shift+=e,t.prelim+=e,t.mod+=e}function ju(n,t,e){return n._tree.ancestor.parent==t.parent?n._tree.ancestor:e}function Hu(n,t){return n.value-t.value}function Fu(n,t){var e=n._pack_next;n._pack_next=t,t._pack_prev=n,t._pack_next=e,e._pack_prev=t}function Ou(n,t){n._pack_next=t,t._pack_prev=n}function Yu(n,t){var e=t.x-n.x,r=t.y-n.y,u=n.r+t.r;return.999*u*u>e*e+r*r}function Iu(n){function t(n){l=Math.min(n.x-n.r,l),f=Math.max(n.x+n.r,f),h=Math.min(n.y-n.r,h),g=Math.max(n.y+n.r,g)}if((e=n.children)&&(s=e.length)){var e,r,u,i,o,a,c,s,l=1/0,f=-1/0,h=1/0,g=-1/0;if(e.forEach(Zu),r=e[0],r.x=-r.r,r.y=0,t(r),s>1&&(u=e[1],u.x=u.r,u.y=0,t(u),s>2))for(i=e[2],$u(r,u,i),t(i),Fu(r,i),r._pack_prev=i,Fu(i,u),u=r._pack_next,o=3;s>o;o++){$u(r,u,i=e[o]);var p=0,v=1,d=1;for(a=u._pack_next;a!==u;a=a._pack_next,v++)if(Yu(a,i)){p=1;break}if(1==p)for(c=r._pack_prev;c!==a._pack_prev&&!Yu(c,i);c=c._pack_prev,d++);p?(d>v||v==d&&u.ro;o++)i=e[o],i.x-=m,i.y-=y,x=Math.max(x,i.r+Math.sqrt(i.x*i.x+i.y*i.y));n.r=x,e.forEach(Vu)}}function Zu(n){n._pack_next=n._pack_prev=n}function Vu(n){delete n._pack_next,delete n._pack_prev}function Xu(n,t,e,r){var u=n.children;if(n.x=t+=r*n.x,n.y=e+=r*n.y,n.r*=r,u)for(var i=-1,o=u.length;++iu&&(e+=u/2,u=0),0>i&&(r+=i/2,i=0),{x:e,y:r,dx:u,dy:i}}function ni(n){var t=n[0],e=n[n.length-1];return e>t?[t,e]:[e,t]}function ti(n){return n.rangeExtent?n.rangeExtent():ni(n.range())}function ei(n,t,e,r){var u=e(n[0],n[1]),i=r(t[0],t[1]);return function(n){return i(u(n))}}function ri(n,t){var e,r=0,u=n.length-1,i=n[r],o=n[u];return i>o&&(e=r,r=u,u=e,e=i,i=o,o=e),n[r]=t.floor(i),n[u]=t.ceil(o),n}function ui(n){return n?{floor:function(t){return Math.floor(t/n)*n},ceil:function(t){return Math.ceil(t/n)*n}}:ss}function ii(n,t,e,r){var u=[],i=[],o=0,a=Math.min(n.length,t.length)-1;for(n[a]2?ii:ei,c=r?ru:eu;return o=u(n,t,c,e),a=u(t,n,c,qr),i}function i(n){return o(n)}var o,a;return i.invert=function(n){return a(n)},i.domain=function(t){return arguments.length?(n=t.map(Number),u()):n},i.range=function(n){return arguments.length?(t=n,u()):t},i.rangeRound=function(n){return i.range(n).interpolate(Jr)},i.clamp=function(n){return arguments.length?(r=n,u()):r},i.interpolate=function(n){return arguments.length?(e=n,u()):e},i.ticks=function(t){return li(n,t)},i.tickFormat=function(t,e){return fi(n,t,e)},i.nice=function(t){return ci(n,t),u()},i.copy=function(){return oi(n,t,e,r)},u()}function ai(n,t){return $o.rebind(n,t,"range","rangeRound","interpolate","clamp")}function ci(n,t){return ri(n,ui(si(n,t)[2]))}function si(n,t){null==t&&(t=10);var e=ni(n),r=e[1]-e[0],u=Math.pow(10,Math.floor(Math.log(r/t)/Math.LN10)),i=t/r*u;return.15>=i?u*=10:.35>=i?u*=5:.75>=i&&(u*=2),e[0]=Math.ceil(e[0]/u)*u,e[1]=Math.floor(e[1]/u)*u+.5*u,e[2]=u,e}function li(n,t){return $o.range.apply($o,si(n,t))}function fi(n,t,e){var r=si(n,t);return $o.format(e?e.replace(uc,function(n,t,e,u,i,o,a,c,s,l){return[t,e,u,i,o,a,c,s||"."+gi(l,r),l].join("")}):",."+hi(r[2])+"f")}function hi(n){return-Math.floor(Math.log(n)/Math.LN10+.01)}function gi(n,t){var e=hi(t[2]);return n in ls?Math.abs(e-hi(Math.max(Math.abs(t[0]),Math.abs(t[1]))))+ +("e"!==n):e-2*("%"===n)}function pi(n,t,e,r){function u(n){return(e?Math.log(0>n?0:n):-Math.log(n>0?0:-n))/Math.log(t)}function i(n){return e?Math.pow(t,n):-Math.pow(t,-n)}function o(t){return n(u(t))}return o.invert=function(t){return i(n.invert(t))},o.domain=function(t){return arguments.length?(e=t[0]>=0,n.domain((r=t.map(Number)).map(u)),o):r},o.base=function(e){return arguments.length?(t=+e,n.domain(r.map(u)),o):t},o.nice=function(){var t=ri(r.map(u),e?Math:hs);return n.domain(t),r=t.map(i),o},o.ticks=function(){var n=ni(r),o=[],a=n[0],c=n[1],s=Math.floor(u(a)),l=Math.ceil(u(c)),f=t%1?2:t;if(isFinite(l-s)){if(e){for(;l>s;s++)for(var h=1;f>h;h++)o.push(i(s)*h);o.push(i(s))}else for(o.push(i(s));s++0;h--)o.push(i(s)*h);for(s=0;o[s]c;l--);o=o.slice(s,l)}return o},o.tickFormat=function(n,t){if(!arguments.length)return fs;arguments.length<2?t=fs:"function"!=typeof t&&(t=$o.format(t));var r,a=Math.max(.1,n/o.ticks().length),c=e?(r=1e-12,Math.ceil):(r=-1e-12,Math.floor);return function(n){return n/i(c(u(n)+r))<=a?t(n):""}},o.copy=function(){return pi(n.copy(),t,e,r)},ai(o,n)}function vi(n,t,e){function r(t){return n(u(t))}var u=di(t),i=di(1/t);return r.invert=function(t){return i(n.invert(t))},r.domain=function(t){return arguments.length?(n.domain((e=t.map(Number)).map(u)),r):e},r.ticks=function(n){return li(e,n)},r.tickFormat=function(n,t){return fi(e,n,t)},r.nice=function(n){return r.domain(ci(e,n))},r.exponent=function(o){return arguments.length?(u=di(t=o),i=di(1/t),n.domain(e.map(u)),r):t},r.copy=function(){return vi(n.copy(),t,e)},ai(r,n)}function di(n){return function(t){return 0>t?-Math.pow(-t,n):Math.pow(t,n)}}function mi(n,t){function e(e){return o[((i.get(e)||"range"===t.t&&i.set(e,n.push(e)))-1)%o.length]}function r(t,e){return $o.range(n.length).map(function(n){return t+e*n})}var i,o,a;return e.domain=function(r){if(!arguments.length)return n;n=[],i=new u;for(var o,a=-1,c=r.length;++ae?[0/0,0/0]:[e>0?u[e-1]:n[0],et?0/0:t/i+n,[t,t+1/i]},r.copy=function(){return xi(n,t,e)},u()}function Mi(n,t){function e(e){return e>=e?t[$o.bisect(n,e)]:void 0}return e.domain=function(t){return arguments.length?(n=t,e):n},e.range=function(n){return arguments.length?(t=n,e):t},e.invertExtent=function(e){return e=t.indexOf(e),[n[e-1],n[e]]},e.copy=function(){return Mi(n,t)},e}function _i(n){function t(n){return+n}return t.invert=t,t.domain=t.range=function(e){return arguments.length?(n=e.map(t),t):n},t.ticks=function(t){return li(n,t)},t.tickFormat=function(t,e){return fi(n,t,e)},t.copy=function(){return _i(n)},t}function bi(n){return n.innerRadius}function wi(n){return n.outerRadius}function Si(n){return n.startAngle}function ki(n){return n.endAngle}function Ei(n){function t(t){function o(){s.push("M",i(n(l),a))}for(var c,s=[],l=[],f=-1,h=t.length,g=pt(e),p=pt(r);++f1&&u.push("H",r[0]),u.join("")}function Li(n){for(var t=0,e=n.length,r=n[0],u=[r[0],",",r[1]];++t1){a=t[1],i=n[c],c++,r+="C"+(u[0]+o[0])+","+(u[1]+o[1])+","+(i[0]-a[0])+","+(i[1]-a[1])+","+i[0]+","+i[1];for(var s=2;s9&&(u=3*t/Math.sqrt(u),o[a]=u*e,o[a+1]=u*r));for(a=-1;++a<=c;)u=(n[Math.min(c,a+1)][0]-n[Math.max(0,a-1)][0])/(6*(1+o[a]*o[a])),i.push([u||0,o[a]*u||0]);return i}function Xi(n){return n.length<3?Ai(n):n[0]+Di(n,Vi(n))}function $i(n){for(var t,e,r,u=-1,i=n.length;++ue?s():(i.active=e,o.event&&o.event.start.call(n,l,t),o.tween.forEach(function(e,r){(r=r.call(n,l,t))&&v.push(r)}),$o.timer(function(){return p.c=c(r||1)?Zt:c,1},0,a),void 0)}function c(r){if(i.active!==e)return s();for(var u=r/g,a=f(u),c=v.length;c>0;)v[--c].call(n,a);return u>=1?(o.event&&o.event.end.call(n,l,t),s()):void 0}function s(){return--i.count?delete i[e]:delete n.__transition__,1}var l=n.__data__,f=o.ease,h=o.delay,g=o.duration,p=Ga,v=[];return p.t=h+a,r>=h?u(r-h):(p.c=u,void 0)},0,a)}}function io(n,t){n.attr("transform",function(n){return"translate("+t(n)+",0)"})}function oo(n,t){n.attr("transform",function(n){return"translate(0,"+t(n)+")"})}function ao(){this._=new Date(arguments.length>1?Date.UTC.apply(this,arguments):arguments[0])}function co(n,t,e){function r(t){var e=n(t),r=i(e,1);return r-t>t-e?e:r}function u(e){return t(e=n(new Ds(e-1)),1),e}function i(n,e){return t(n=new Ds(+n),e),n}function o(n,r,i){var o=u(n),a=[];if(i>1)for(;r>o;)e(o)%i||a.push(new Date(+o)),t(o,1);else for(;r>o;)a.push(new Date(+o)),t(o,1);return a}function a(n,t,e){try{Ds=ao;var r=new ao;return r._=n,o(r,t,e)}finally{Ds=Date}}n.floor=n,n.round=r,n.ceil=u,n.offset=i,n.range=o;var c=n.utc=so(n);return c.floor=c,c.round=so(r),c.ceil=so(u),c.offset=so(i),c.range=a,n}function so(n){return function(t,e){try{Ds=ao;var r=new ao;return r._=t,n(r,e)._}finally{Ds=Date}}}function lo(n){function t(t){for(var r,u,i,o=[],a=-1,c=0;++aa;){if(r>=s)return-1;if(u=t.charCodeAt(a++),37===u){if(o=t.charAt(a++),i=el[o in nl?t.charAt(a++):o],!i||(r=i(n,e,r))<0)return-1}else if(u!=e.charCodeAt(r++))return-1}return r}function ho(n){return new RegExp("^(?:"+n.map($o.requote).join("|")+")","i")}function go(n){for(var t=new u,e=-1,r=n.length;++en?"-":"",u=(r?-n:n)+"",i=u.length;return r+(e>i?new Array(e-i+1).join(t)+u:u)}function vo(n,t,e){$s.lastIndex=0;var r=$s.exec(t.substring(e));return r?(n.w=Bs.get(r[0].toLowerCase()),e+r[0].length):-1}function mo(n,t,e){Vs.lastIndex=0;var r=Vs.exec(t.substring(e));return r?(n.w=Xs.get(r[0].toLowerCase()),e+r[0].length):-1}function yo(n,t,e){rl.lastIndex=0;var r=rl.exec(t.substring(e,e+1));return r?(n.w=+r[0],e+r[0].length):-1}function xo(n,t,e){rl.lastIndex=0;var r=rl.exec(t.substring(e));return r?(n.U=+r[0],e+r[0].length):-1}function Mo(n,t,e){rl.lastIndex=0;var r=rl.exec(t.substring(e));return r?(n.W=+r[0],e+r[0].length):-1}function _o(n,t,e){Gs.lastIndex=0;var r=Gs.exec(t.substring(e));return r?(n.m=Ks.get(r[0].toLowerCase()),e+r[0].length):-1}function bo(n,t,e){Ws.lastIndex=0;var r=Ws.exec(t.substring(e));return r?(n.m=Js.get(r[0].toLowerCase()),e+r[0].length):-1}function wo(n,t,e){return fo(n,tl.c.toString(),t,e)}function So(n,t,e){return fo(n,tl.x.toString(),t,e)}function ko(n,t,e){return fo(n,tl.X.toString(),t,e)}function Eo(n,t,e){rl.lastIndex=0;var r=rl.exec(t.substring(e,e+4));return r?(n.y=+r[0],e+r[0].length):-1}function Ao(n,t,e){rl.lastIndex=0;var r=rl.exec(t.substring(e,e+2));return r?(n.y=No(+r[0]),e+r[0].length):-1}function Co(n,t,e){return/^[+-]\d{4}$/.test(t=t.substring(e,e+5))?(n.Z=+t,e+5):-1}function No(n){return n+(n>68?1900:2e3)}function Lo(n,t,e){rl.lastIndex=0;var r=rl.exec(t.substring(e,e+2));return r?(n.m=r[0]-1,e+r[0].length):-1}function To(n,t,e){rl.lastIndex=0;var r=rl.exec(t.substring(e,e+2));return r?(n.d=+r[0],e+r[0].length):-1}function qo(n,t,e){rl.lastIndex=0;var r=rl.exec(t.substring(e,e+3));return r?(n.j=+r[0],e+r[0].length):-1}function zo(n,t,e){rl.lastIndex=0;var r=rl.exec(t.substring(e,e+2));return r?(n.H=+r[0],e+r[0].length):-1}function Ro(n,t,e){rl.lastIndex=0;var r=rl.exec(t.substring(e,e+2));return r?(n.M=+r[0],e+r[0].length):-1}function Do(n,t,e){rl.lastIndex=0;var r=rl.exec(t.substring(e,e+2));return r?(n.S=+r[0],e+r[0].length):-1}function Po(n,t,e){rl.lastIndex=0;var r=rl.exec(t.substring(e,e+3));return r?(n.L=+r[0],e+r[0].length):-1}function Uo(n,t,e){var r=ul.get(t.substring(e,e+=2).toLowerCase());return null==r?-1:(n.p=r,e)}function jo(n){var t=n.getTimezoneOffset(),e=t>0?"-":"+",r=~~(aa(t)/60),u=aa(t)%60;return e+po(r,"0",2)+po(u,"0",2)}function Ho(n,t,e){Qs.lastIndex=0;var r=Qs.exec(t.substring(e,e+1));return r?e+r[0].length:-1}function Fo(n){function t(n){try{Ds=ao;var t=new Ds;return t._=n,e(t)}finally{Ds=Date}}var e=lo(n);return t.parse=function(n){try{Ds=ao;var t=e.parse(n);return t&&t._}finally{Ds=Date}},t.toString=e.toString,t}function Oo(n){return n.toISOString()}function Yo(n,t,e){function r(t){return n(t)}function u(n,e){var r=n[1]-n[0],u=r/e,i=$o.bisect(ol,u);return i==ol.length?[t.year,si(n.map(function(n){return n/31536e6}),e)[2]]:i?t[u/ol[i-1]1?{floor:function(t){for(;e(t=n.floor(t));)t=Io(t-1);return t},ceil:function(t){for(;e(t=n.ceil(t));)t=Io(+t+1);return t}}:n))},r.ticks=function(n,t){var e=ni(r.domain()),i=null==n?u(e,10):"number"==typeof n?u(e,n):!n.range&&[{range:n},t];return i&&(n=i[0],t=i[1]),n.range(e[0],Io(+e[1]+1),1>t?1:t)},r.tickFormat=function(){return e},r.copy=function(){return Yo(n.copy(),t,e)},ai(r,n)}function Io(n){return new Date(n)}function Zo(n){return function(t){for(var e=n.length-1,r=n[e];!r[1](t);)r=n[--e];return r[0](t)}}function Vo(n){return JSON.parse(n.responseText)}function Xo(n){var t=Jo.createRange();return t.selectNode(Jo.body),t.createContextualFragment(n.responseText)}var $o={version:"3.3.9"};Date.now||(Date.now=function(){return+new Date});var Bo=[].slice,Wo=function(n){return Bo.call(n)},Jo=document,Go=Jo.documentElement,Ko=window;try{Wo(Go.childNodes)[0].nodeType}catch(Qo){Wo=function(n){for(var t=n.length,e=new Array(t);t--;)e[t]=n[t];return e}}try{Jo.createElement("div").style.setProperty("opacity",0,"")}catch(na){var ta=Ko.Element.prototype,ea=ta.setAttribute,ra=ta.setAttributeNS,ua=Ko.CSSStyleDeclaration.prototype,ia=ua.setProperty;ta.setAttribute=function(n,t){ea.call(this,n,t+"")},ta.setAttributeNS=function(n,t,e){ra.call(this,n,t,e+"")},ua.setProperty=function(n,t,e){ia.call(this,n,t+"",e)}}$o.ascending=function(n,t){return t>n?-1:n>t?1:n>=t?0:0/0},$o.descending=function(n,t){return n>t?-1:t>n?1:t>=n?0:0/0},$o.min=function(n,t){var e,r,u=-1,i=n.length;if(1===arguments.length){for(;++u=e);)e=void 0;for(;++ur&&(e=r)}else{for(;++u=e);)e=void 0;for(;++ur&&(e=r)}return e},$o.max=function(n,t){var e,r,u=-1,i=n.length;if(1===arguments.length){for(;++u=e);)e=void 0;for(;++ue&&(e=r)}else{for(;++u=e);)e=void 0;for(;++ue&&(e=r)}return e},$o.extent=function(n,t){var e,r,u,i=-1,o=n.length;if(1===arguments.length){for(;++i=e);)e=u=void 0;for(;++ir&&(e=r),r>u&&(u=r))}else{for(;++i=e);)e=void 0;for(;++ir&&(e=r),r>u&&(u=r))}return[e,u]},$o.sum=function(n,t){var e,r=0,u=n.length,i=-1;if(1===arguments.length)for(;++i1&&(t=t.map(e)),t=t.filter(n),t.length?$o.quantile(t.sort($o.ascending),.5):void 0},$o.bisector=function(n){return{left:function(t,e,r,u){for(arguments.length<3&&(r=0),arguments.length<4&&(u=t.length);u>r;){var i=r+u>>>1;n.call(t,t[i],i)r;){var i=r+u>>>1;er?0:r);r>e;)i[e]=[t=u,u=n[++e]];return i},$o.zip=function(){if(!(u=arguments.length))return[];for(var n=-1,e=$o.min(arguments,t),r=new Array(e);++n=0;)for(r=n[u],t=r.length;--t>=0;)e[--o]=r[t];return e};var aa=Math.abs;$o.range=function(n,t,r){if(arguments.length<3&&(r=1,arguments.length<2&&(t=n,n=0)),1/0===(t-n)/r)throw new Error("infinite range");var u,i=[],o=e(aa(r)),a=-1;if(n*=o,t*=o,r*=o,0>r)for(;(u=n+r*++a)>t;)i.push(u/o);else for(;(u=n+r*++a)=o.length)return r?r.call(i,a):e?a.sort(e):a;for(var s,l,f,h,g=-1,p=a.length,v=o[c++],d=new u;++g=o.length)return n;var r=[],u=a[e++];return n.forEach(function(n,u){r.push({key:n,values:t(u,e)})}),u?r.sort(function(n,t){return u(n.key,t.key)}):r}var e,r,i={},o=[],a=[];return i.map=function(t,e){return n(e,t,0)},i.entries=function(e){return t(n($o.map,e,0),0)},i.key=function(n){return o.push(n),i},i.sortKeys=function(n){return a[o.length-1]=n,i},i.sortValues=function(n){return e=n,i},i.rollup=function(n){return r=n,i},i},$o.set=function(n){var t=new i;if(n)for(var e=0,r=n.length;r>e;++e)t.add(n[e]);return t},r(i,{has:function(n){return ca+n in this},add:function(n){return this[ca+n]=!0,n},remove:function(n){return n=ca+n,n in this&&delete this[n]},values:function(){var n=[];return this.forEach(function(t){n.push(t)}),n},forEach:function(n){for(var t in this)t.charCodeAt(0)===sa&&n.call(this,t.substring(1))}}),$o.behavior={},$o.rebind=function(n,t){for(var e,r=1,u=arguments.length;++r=0&&(r=n.substring(e+1),n=n.substring(0,e)),n)return arguments.length<2?this[n].on(r):this[n].on(r,t);if(2===arguments.length){if(null==t)for(n in this)this.hasOwnProperty(n)&&this[n].on(r,null);return this}},$o.event=null,$o.requote=function(n){return n.replace(fa,"\\$&")};var fa=/[\\\^\$\*\+\?\|\[\]\(\)\.\{\}]/g,ha={}.__proto__?function(n,t){n.__proto__=t}:function(n,t){for(var e in t)n[e]=t[e]},ga=function(n,t){return t.querySelector(n)},pa=function(n,t){return t.querySelectorAll(n)},va=Go[a(Go,"matchesSelector")],da=function(n,t){return va.call(n,t)};"function"==typeof Sizzle&&(ga=function(n,t){return Sizzle(n,t)[0]||null},pa=function(n,t){return Sizzle.uniqueSort(Sizzle(n,t))},da=Sizzle.matchesSelector),$o.selection=function(){return Ma};var ma=$o.selection.prototype=[];ma.select=function(n){var t,e,r,u,i=[];n=v(n);for(var o=-1,a=this.length;++o=0&&(e=n.substring(0,t),n=n.substring(t+1)),ya.hasOwnProperty(e)?{space:ya[e],local:n}:n}},ma.attr=function(n,t){if(arguments.length<2){if("string"==typeof n){var e=this.node();return n=$o.ns.qualify(n),n.local?e.getAttributeNS(n.space,n.local):e.getAttribute(n)}for(t in n)this.each(m(t,n[t]));return this}return this.each(m(n,t))},ma.classed=function(n,t){if(arguments.length<2){if("string"==typeof n){var e=this.node(),r=(n=n.trim().split(/^|\s+/g)).length,u=-1;if(t=e.classList){for(;++ur){if("string"!=typeof n){2>r&&(t="");for(e in n)this.each(b(e,n[e],t));return this}if(2>r)return Ko.getComputedStyle(this.node(),null).getPropertyValue(n);e=""}return this.each(b(n,t,e))},ma.property=function(n,t){if(arguments.length<2){if("string"==typeof n)return this.node()[n];for(t in n)this.each(w(t,n[t]));return this}return this.each(w(n,t))},ma.text=function(n){return arguments.length?this.each("function"==typeof n?function(){var t=n.apply(this,arguments);this.textContent=null==t?"":t}:null==n?function(){this.textContent=""}:function(){this.textContent=n}):this.node().textContent},ma.html=function(n){return arguments.length?this.each("function"==typeof n?function(){var t=n.apply(this,arguments);this.innerHTML=null==t?"":t}:null==n?function(){this.innerHTML=""}:function(){this.innerHTML=n}):this.node().innerHTML},ma.append=function(n){return n=S(n),this.select(function(){return this.appendChild(n.apply(this,arguments))})},ma.insert=function(n,t){return n=S(n),t=v(t),this.select(function(){return this.insertBefore(n.apply(this,arguments),t.apply(this,arguments)||null)})},ma.remove=function(){return this.each(function(){var n=this.parentNode;n&&n.removeChild(this)})},ma.data=function(n,t){function e(n,e){var r,i,o,a=n.length,f=e.length,h=Math.min(a,f),g=new Array(f),p=new Array(f),v=new Array(a);if(t){var d,m=new u,y=new u,x=[];for(r=-1;++rr;++r)p[r]=k(e[r]);for(;a>r;++r)v[r]=n[r]}p.update=g,p.parentNode=g.parentNode=v.parentNode=n.parentNode,c.push(p),s.push(g),l.push(v)}var r,i,o=-1,a=this.length;if(!arguments.length){for(n=new Array(a=(r=this[0]).length);++oi;i++){u.push(t=[]),t.parentNode=(e=this[i]).parentNode;for(var a=0,c=e.length;c>a;a++)(r=e[a])&&n.call(r,r.__data__,a)&&t.push(r)}return p(u)},ma.order=function(){for(var n=-1,t=this.length;++n=0;)(e=r[u])&&(i&&i!==e.nextSibling&&i.parentNode.insertBefore(e,i),i=e);return this},ma.sort=function(n){n=A.apply(this,arguments);for(var t=-1,e=this.length;++tn;n++)for(var e=this[n],r=0,u=e.length;u>r;r++){var i=e[r];if(i)return i}return null},ma.size=function(){var n=0;return this.each(function(){++n}),n};var xa=[];$o.selection.enter=N,$o.selection.enter.prototype=xa,xa.append=ma.append,xa.empty=ma.empty,xa.node=ma.node,xa.call=ma.call,xa.size=ma.size,xa.select=function(n){for(var t,e,r,u,i,o=[],a=-1,c=this.length;++ar){if("string"!=typeof n){2>r&&(t=!1);for(e in n)this.each(q(e,n[e],t));return this}if(2>r)return(r=this.node()["__on"+n])&&r._;e=!1}return this.each(q(n,t,e))};var _a=$o.map({mouseenter:"mouseover",mouseleave:"mouseout"});_a.forEach(function(n){"on"+n in Jo&&_a.remove(n)});var ba="onselectstart"in Jo?null:a(Go.style,"userSelect"),wa=0;$o.mouse=function(n){return P(n,h())};var Sa=/WebKit/.test(Ko.navigator.userAgent)?-1:0;$o.touches=function(n,t){return arguments.length<2&&(t=h().touches),t?Wo(t).map(function(t){var e=P(n,t);return e.identifier=t.identifier,e}):[]},$o.behavior.drag=function(){function n(){this.on("mousedown.drag",o).on("touchstart.drag",a)}function t(){return $o.event.changedTouches[0].identifier}function e(n,t){return $o.touches(n).filter(function(n){return n.identifier===t})[0]}function r(n,t,e,r){return function(){function o(){var n=t(l,g),e=n[0]-v[0],r=n[1]-v[1];d|=e|r,v=n,f({type:"drag",x:n[0]+c[0],y:n[1]+c[1],dx:e,dy:r})}function a(){m.on(e+"."+p,null).on(r+"."+p,null),y(d&&$o.event.target===h),f({type:"dragend"})}var c,s=this,l=s.parentNode,f=u.of(s,arguments),h=$o.event.target,g=n(),p=null==g?"drag":"drag-"+g,v=t(l,g),d=0,m=$o.select(Ko).on(e+"."+p,o).on(r+"."+p,a),y=D();i?(c=i.apply(s,arguments),c=[c.x-v[0],c.y-v[1]]):c=[0,0],f({type:"dragstart"})}}var u=g(n,"drag","dragstart","dragend"),i=null,o=r(c,$o.mouse,"mousemove","mouseup"),a=r(t,e,"touchmove","touchend");return n.origin=function(t){return arguments.length?(i=t,n):i},$o.rebind(n,u,"on")};var ka=Math.PI,Ea=2*ka,Aa=ka/2,Ca=1e-6,Na=Ca*Ca,La=ka/180,Ta=180/ka,qa=Math.SQRT2,za=2,Ra=4;$o.interpolateZoom=function(n,t){function e(n){var t=n*y;if(m){var e=O(v),o=i/(za*h)*(e*Y(qa*t+v)-F(v));return[r+o*s,u+o*l,i*e/O(qa*t+v)]}return[r+n*s,u+n*l,i*Math.exp(qa*t)]}var r=n[0],u=n[1],i=n[2],o=t[0],a=t[1],c=t[2],s=o-r,l=a-u,f=s*s+l*l,h=Math.sqrt(f),g=(c*c-i*i+Ra*f)/(2*i*za*h),p=(c*c-i*i-Ra*f)/(2*c*za*h),v=Math.log(Math.sqrt(g*g+1)-g),d=Math.log(Math.sqrt(p*p+1)-p),m=d-v,y=(m||Math.log(c/i))/qa;return e.duration=1e3*y,e},$o.behavior.zoom=function(){function n(n){n.on(A,s).on(Ua+".zoom",h).on(C,p).on("dblclick.zoom",v).on(L,l)}function t(n){return[(n[0]-S.x)/S.k,(n[1]-S.y)/S.k]}function e(n){return[n[0]*S.k+S.x,n[1]*S.k+S.y]}function r(n){S.k=Math.max(E[0],Math.min(E[1],n))}function u(n,t){t=e(t),S.x+=n[0]-t[0],S.y+=n[1]-t[1]}function i(){_&&_.domain(M.range().map(function(n){return(n-S.x)/S.k}).map(M.invert)),w&&w.domain(b.range().map(function(n){return(n-S.y)/S.k}).map(b.invert))}function o(n){n({type:"zoomstart"})}function a(n){i(),n({type:"zoom",scale:S.k,translate:[S.x,S.y]})}function c(n){n({type:"zoomend"})}function s(){function n(){l=1,u($o.mouse(r),h),a(i)}function e(){f.on(C,Ko===r?p:null).on(N,null),g(l&&$o.event.target===s),c(i)}var r=this,i=q.of(r,arguments),s=$o.event.target,l=0,f=$o.select(Ko).on(C,n).on(N,e),h=t($o.mouse(r)),g=D();T.call(r),o(i)}function l(){function n(){var n=$o.touches(p);return g=S.k,n.forEach(function(n){n.identifier in d&&(d[n.identifier]=t(n))}),n}function e(){for(var t=$o.event.changedTouches,e=0,i=t.length;i>e;++e)d[t[e].identifier]=null;var o=n(),c=Date.now();if(1===o.length){if(500>c-x){var s=o[0],l=d[s.identifier];r(2*S.k),u(s,l),f(),a(v)}x=c}else if(o.length>1){var s=o[0],h=o[1],g=s[0]-h[0],p=s[1]-h[1];m=g*g+p*p}}function i(){for(var n,t,e,i,o=$o.touches(p),c=0,s=o.length;s>c;++c,i=null)if(e=o[c],i=d[e.identifier]){if(t)break;n=e,t=i}if(i){var l=(l=e[0]-n[0])*l+(l=e[1]-n[1])*l,f=m&&Math.sqrt(l/m);n=[(n[0]+e[0])/2,(n[1]+e[1])/2],t=[(t[0]+i[0])/2,(t[1]+i[1])/2],r(f*g)}x=null,u(n,t),a(v)}function h(){if($o.event.touches.length){for(var t=$o.event.changedTouches,e=0,r=t.length;r>e;++e)delete d[t[e].identifier];for(var u in d)return void n()}b.on(M,null).on(_,null),w.on(A,s).on(L,l),k(),c(v)}var g,p=this,v=q.of(p,arguments),d={},m=0,y=$o.event.changedTouches[0].identifier,M="touchmove.zoom-"+y,_="touchend.zoom-"+y,b=$o.select(Ko).on(M,i).on(_,h),w=$o.select(p).on(A,null).on(L,e),k=D();T.call(p),e(),o(v)}function h(){var n=q.of(this,arguments);y?clearTimeout(y):(T.call(this),o(n)),y=setTimeout(function(){y=null,c(n)},50),f();var e=m||$o.mouse(this);d||(d=t(e)),r(Math.pow(2,.002*Da())*S.k),u(e,d),a(n)}function p(){d=null}function v(){var n=q.of(this,arguments),e=$o.mouse(this),i=t(e),s=Math.log(S.k)/Math.LN2;o(n),r(Math.pow(2,$o.event.shiftKey?Math.ceil(s)-1:Math.floor(s)+1)),u(e,i),a(n),c(n)}var d,m,y,x,M,_,b,w,S={x:0,y:0,k:1},k=[960,500],E=Pa,A="mousedown.zoom",C="mousemove.zoom",N="mouseup.zoom",L="touchstart.zoom",q=g(n,"zoomstart","zoom","zoomend");return n.event=function(n){n.each(function(){var n=q.of(this,arguments),t=S;Ss?$o.select(this).transition().each("start.zoom",function(){S=this.__chart__||{x:0,y:0,k:1},o(n)}).tween("zoom:zoom",function(){var e=k[0],r=k[1],u=e/2,i=r/2,o=$o.interpolateZoom([(u-S.x)/S.k,(i-S.y)/S.k,e/S.k],[(u-t.x)/t.k,(i-t.y)/t.k,e/t.k]);return function(t){var r=o(t),c=e/r[2];this.__chart__=S={x:u-r[0]*c,y:i-r[1]*c,k:c},a(n)}}).each("end.zoom",function(){c(n)}):(this.__chart__=S,o(n),a(n),c(n))})},n.translate=function(t){return arguments.length?(S={x:+t[0],y:+t[1],k:S.k},i(),n):[S.x,S.y]},n.scale=function(t){return arguments.length?(S={x:S.x,y:S.y,k:+t},i(),n):S.k},n.scaleExtent=function(t){return arguments.length?(E=null==t?Pa:[+t[0],+t[1]],n):E},n.center=function(t){return arguments.length?(m=t&&[+t[0],+t[1]],n):m},n.size=function(t){return arguments.length?(k=t&&[+t[0],+t[1]],n):k},n.x=function(t){return arguments.length?(_=t,M=t.copy(),S={x:0,y:0,k:1},n):_},n.y=function(t){return arguments.length?(w=t,b=t.copy(),S={x:0,y:0,k:1},n):w},$o.rebind(n,q,"on")};var Da,Pa=[0,1/0],Ua="onwheel"in Jo?(Da=function(){return-$o.event.deltaY*($o.event.deltaMode?120:1)},"wheel"):"onmousewheel"in Jo?(Da=function(){return $o.event.wheelDelta},"mousewheel"):(Da=function(){return-$o.event.detail},"MozMousePixelScroll");Z.prototype.toString=function(){return this.rgb()+""},$o.hsl=function(n,t,e){return 1===arguments.length?n instanceof X?V(n.h,n.s,n.l):st(""+n,lt,V):V(+n,+t,+e)};var ja=X.prototype=new Z;ja.brighter=function(n){return n=Math.pow(.7,arguments.length?n:1),V(this.h,this.s,this.l/n)},ja.darker=function(n){return n=Math.pow(.7,arguments.length?n:1),V(this.h,this.s,n*this.l)},ja.rgb=function(){return $(this.h,this.s,this.l)},$o.hcl=function(n,t,e){return 1===arguments.length?n instanceof W?B(n.h,n.c,n.l):n instanceof K?nt(n.l,n.a,n.b):nt((n=ft((n=$o.rgb(n)).r,n.g,n.b)).l,n.a,n.b):B(+n,+t,+e)};var Ha=W.prototype=new Z;Ha.brighter=function(n){return B(this.h,this.c,Math.min(100,this.l+Fa*(arguments.length?n:1)))},Ha.darker=function(n){return B(this.h,this.c,Math.max(0,this.l-Fa*(arguments.length?n:1)))},Ha.rgb=function(){return J(this.h,this.c,this.l).rgb()},$o.lab=function(n,t,e){return 1===arguments.length?n instanceof K?G(n.l,n.a,n.b):n instanceof W?J(n.l,n.c,n.h):ft((n=$o.rgb(n)).r,n.g,n.b):G(+n,+t,+e)};var Fa=18,Oa=.95047,Ya=1,Ia=1.08883,Za=K.prototype=new Z;Za.brighter=function(n){return G(Math.min(100,this.l+Fa*(arguments.length?n:1)),this.a,this.b)},Za.darker=function(n){return G(Math.max(0,this.l-Fa*(arguments.length?n:1)),this.a,this.b)},Za.rgb=function(){return Q(this.l,this.a,this.b)},$o.rgb=function(n,t,e){return 1===arguments.length?n instanceof at?ot(n.r,n.g,n.b):st(""+n,ot,$):ot(~~n,~~t,~~e)};var Va=at.prototype=new Z;Va.brighter=function(n){n=Math.pow(.7,arguments.length?n:1);var t=this.r,e=this.g,r=this.b,u=30;return t||e||r?(t&&u>t&&(t=u),e&&u>e&&(e=u),r&&u>r&&(r=u),ot(Math.min(255,~~(t/n)),Math.min(255,~~(e/n)),Math.min(255,~~(r/n)))):ot(u,u,u)},Va.darker=function(n){return n=Math.pow(.7,arguments.length?n:1),ot(~~(n*this.r),~~(n*this.g),~~(n*this.b))},Va.hsl=function(){return lt(this.r,this.g,this.b)},Va.toString=function(){return"#"+ct(this.r)+ct(this.g)+ct(this.b)};var Xa=$o.map({aliceblue:15792383,antiquewhite:16444375,aqua:65535,aquamarine:8388564,azure:15794175,beige:16119260,bisque:16770244,black:0,blanchedalmond:16772045,blue:255,blueviolet:9055202,brown:10824234,burlywood:14596231,cadetblue:6266528,chartreuse:8388352,chocolate:13789470,coral:16744272,cornflowerblue:6591981,cornsilk:16775388,crimson:14423100,cyan:65535,darkblue:139,darkcyan:35723,darkgoldenrod:12092939,darkgray:11119017,darkgreen:25600,darkgrey:11119017,darkkhaki:12433259,darkmagenta:9109643,darkolivegreen:5597999,darkorange:16747520,darkorchid:10040012,darkred:9109504,darksalmon:15308410,darkseagreen:9419919,darkslateblue:4734347,darkslategray:3100495,darkslategrey:3100495,darkturquoise:52945,darkviolet:9699539,deeppink:16716947,deepskyblue:49151,dimgray:6908265,dimgrey:6908265,dodgerblue:2003199,firebrick:11674146,floralwhite:16775920,forestgreen:2263842,fuchsia:16711935,gainsboro:14474460,ghostwhite:16316671,gold:16766720,goldenrod:14329120,gray:8421504,green:32768,greenyellow:11403055,grey:8421504,honeydew:15794160,hotpink:16738740,indianred:13458524,indigo:4915330,ivory:16777200,khaki:15787660,lavender:15132410,lavenderblush:16773365,lawngreen:8190976,lemonchiffon:16775885,lightblue:11393254,lightcoral:15761536,lightcyan:14745599,lightgoldenrodyellow:16448210,lightgray:13882323,lightgreen:9498256,lightgrey:13882323,lightpink:16758465,lightsalmon:16752762,lightseagreen:2142890,lightskyblue:8900346,lightslategray:7833753,lightslategrey:7833753,lightsteelblue:11584734,lightyellow:16777184,lime:65280,limegreen:3329330,linen:16445670,magenta:16711935,maroon:8388608,mediumaquamarine:6737322,mediumblue:205,mediumorchid:12211667,mediumpurple:9662683,mediumseagreen:3978097,mediumslateblue:8087790,mediumspringgreen:64154,mediumturquoise:4772300,mediumvioletred:13047173,midnightblue:1644912,mintcream:16121850,mistyrose:16770273,moccasin:16770229,navajowhite:16768685,navy:128,oldlace:16643558,olive:8421376,olivedrab:7048739,orange:16753920,orangered:16729344,orchid:14315734,palegoldenrod:15657130,palegreen:10025880,paleturquoise:11529966,palevioletred:14381203,papayawhip:16773077,peachpuff:16767673,peru:13468991,pink:16761035,plum:14524637,powderblue:11591910,purple:8388736,red:16711680,rosybrown:12357519,royalblue:4286945,saddlebrown:9127187,salmon:16416882,sandybrown:16032864,seagreen:3050327,seashell:16774638,sienna:10506797,silver:12632256,skyblue:8900331,slateblue:6970061,slategray:7372944,slategrey:7372944,snow:16775930,springgreen:65407,steelblue:4620980,tan:13808780,teal:32896,thistle:14204888,tomato:16737095,turquoise:4251856,violet:15631086,wheat:16113331,white:16777215,whitesmoke:16119285,yellow:16776960,yellowgreen:10145074});Xa.forEach(function(n,t){Xa.set(n,ut(t))}),$o.functor=pt,$o.xhr=dt(vt),$o.dsv=function(n,t){function e(n,e,i){arguments.length<3&&(i=e,e=null);var o=$o.xhr(n,t,i);return o.row=function(n){return arguments.length?o.response(null==(e=n)?r:u(n)):e},o.row(e)}function r(n){return e.parse(n.responseText)}function u(n){return function(t){return e.parse(t.responseText,n)}}function o(t){return t.map(a).join(n)}function a(n){return c.test(n)?'"'+n.replace(/\"/g,'""')+'"':n}var c=new RegExp('["'+n+"\n]"),s=n.charCodeAt(0);return e.parse=function(n,t){var r;return e.parseRows(n,function(n,e){if(r)return r(n,e-1);var u=new Function("d","return {"+n.map(function(n,t){return JSON.stringify(n)+": d["+t+"]"}).join(",")+"}");r=t?function(n,e){return t(u(n),e)}:u})},e.parseRows=function(n,t){function e(){if(l>=c)return o;if(u)return u=!1,i;var t=l;if(34===n.charCodeAt(t)){for(var e=t;e++l;){var r=n.charCodeAt(l++),a=1;if(10===r)u=!0;else if(13===r)u=!0,10===n.charCodeAt(l)&&(++l,++a);else if(r!==s)continue;return n.substring(t,l-a)}return n.substring(t)}for(var r,u,i={},o={},a=[],c=n.length,l=0,f=0;(r=e())!==o;){for(var h=[];r!==i&&r!==o;)h.push(r),r=e();(!t||(h=t(h,f++)))&&a.push(h)}return a},e.format=function(t){if(Array.isArray(t[0]))return e.formatRows(t);var r=new i,u=[];return t.forEach(function(n){for(var t in n)r.has(t)||u.push(r.add(t))}),[u.map(a).join(n)].concat(t.map(function(t){return u.map(function(n){return a(t[n])}).join(n)})).join("\n")},e.formatRows=function(n){return n.map(o).join("\n")},e},$o.csv=$o.dsv(",","text/csv"),$o.tsv=$o.dsv(" ","text/tab-separated-values");var $a,Ba,Wa,Ja,Ga,Ka=Ko[a(Ko,"requestAnimationFrame")]||function(n){setTimeout(n,17)};$o.timer=function(n,t,e){var r=arguments.length;2>r&&(t=0),3>r&&(e=Date.now());var u=e+t,i={c:n,t:u,f:!1,n:null};Ba?Ba.n=i:$a=i,Ba=i,Wa||(Ja=clearTimeout(Ja),Wa=1,Ka(xt))},$o.timer.flush=function(){Mt(),_t()};var Qa=".",nc=",",tc=[3,3],ec="$",rc=["y","z","a","f","p","n","\xb5","m","","k","M","G","T","P","E","Z","Y"].map(bt);$o.formatPrefix=function(n,t){var e=0;return n&&(0>n&&(n*=-1),t&&(n=$o.round(n,wt(n,t))),e=1+Math.floor(1e-12+Math.log(n)/Math.LN10),e=Math.max(-24,Math.min(24,3*Math.floor((0>=e?e+1:e-1)/3)))),rc[8+e/3]},$o.round=function(n,t){return t?Math.round(n*(t=Math.pow(10,t)))/t:Math.round(n)},$o.format=function(n){var t=uc.exec(n),e=t[1]||" ",r=t[2]||">",u=t[3]||"",i=t[4]||"",o=t[5],a=+t[6],c=t[7],s=t[8],l=t[9],f=1,h="",g=!1;switch(s&&(s=+s.substring(1)),(o||"0"===e&&"="===r)&&(o=e="0",r="=",c&&(a-=Math.floor((a-1)/4))),l){case"n":c=!0,l="g";break;case"%":f=100,h="%",l="f";break;case"p":f=100,h="%",l="r";break;case"b":case"o":case"x":case"X":"#"===i&&(i="0"+l.toLowerCase());case"c":case"d":g=!0,s=0;break;case"s":f=-1,l="r"}"#"===i?i="":"$"===i&&(i=ec),"r"!=l||s||(l="g"),null!=s&&("g"==l?s=Math.max(1,Math.min(21,s)):("e"==l||"f"==l)&&(s=Math.max(0,Math.min(20,s)))),l=ic.get(l)||St;var p=o&&c;return function(n){if(g&&n%1)return"";var t=0>n||0===n&&0>1/n?(n=-n,"-"):u;if(0>f){var v=$o.formatPrefix(n,s);n=v.scale(n),h=v.symbol}else n*=f;n=l(n,s);var d=n.lastIndexOf("."),m=0>d?n:n.substring(0,d),y=0>d?"":Qa+n.substring(d+1);!o&&c&&(m=oc(m));var x=i.length+m.length+y.length+(p?0:t.length),M=a>x?new Array(x=a-x+1).join(e):"";return p&&(m=oc(M+m)),t+=i,n=m+y,("<"===r?t+n+M:">"===r?M+t+n:"^"===r?M.substring(0,x>>=1)+t+n+M.substring(x):t+(p?n:M+n))+h}};var uc=/(?:([^{])?([<>=^]))?([+\- ])?([$#])?(0)?(\d+)?(,)?(\.-?\d+)?([a-z%])?/i,ic=$o.map({b:function(n){return n.toString(2)},c:function(n){return String.fromCharCode(n)},o:function(n){return n.toString(8)},x:function(n){return n.toString(16)},X:function(n){return n.toString(16).toUpperCase()},g:function(n,t){return n.toPrecision(t)},e:function(n,t){return n.toExponential(t)},f:function(n,t){return n.toFixed(t)},r:function(n,t){return(n=$o.round(n,wt(n,t))).toFixed(Math.max(0,Math.min(20,wt(n*(1+1e-15),t))))}}),oc=vt;if(tc){var ac=tc.length;oc=function(n){for(var t=n.length,e=[],r=0,u=tc[0];t>0&&u>0;)e.push(n.substring(t-=u,t+u)),u=tc[r=(r+1)%ac];return e.reverse().join(nc)}}$o.geo={},kt.prototype={s:0,t:0,add:function(n){Et(n,this.t,cc),Et(cc.s,this.s,this),this.s?this.t+=cc.t:this.s=cc.t},reset:function(){this.s=this.t=0},valueOf:function(){return this.s}};var cc=new kt;$o.geo.stream=function(n,t){n&&sc.hasOwnProperty(n.type)?sc[n.type](n,t):At(n,t)};var sc={Feature:function(n,t){At(n.geometry,t)},FeatureCollection:function(n,t){for(var e=n.features,r=-1,u=e.length;++rn?4*ka+n:n,gc.lineStart=gc.lineEnd=gc.point=c}};$o.geo.bounds=function(){function n(n,t){x.push(M=[l=n,h=n]),f>t&&(f=t),t>g&&(g=t)}function t(t,e){var r=Tt([t*La,e*La]);if(m){var u=zt(m,r),i=[u[1],-u[0],0],o=zt(i,u);Pt(o),o=Ut(o);var c=t-p,s=c>0?1:-1,v=o[0]*Ta*s,d=aa(c)>180;if(d^(v>s*p&&s*t>v)){var y=o[1]*Ta;y>g&&(g=y)}else if(v=(v+360)%360-180,d^(v>s*p&&s*t>v)){var y=-o[1]*Ta;f>y&&(f=y)}else f>e&&(f=e),e>g&&(g=e);d?p>t?a(l,t)>a(l,h)&&(h=t):a(t,h)>a(l,h)&&(l=t):h>=l?(l>t&&(l=t),t>h&&(h=t)):t>p?a(l,t)>a(l,h)&&(h=t):a(t,h)>a(l,h)&&(l=t)}else n(t,e);m=r,p=t}function e(){_.point=t}function r(){M[0]=l,M[1]=h,_.point=n,m=null}function u(n,e){if(m){var r=n-p;y+=aa(r)>180?r+(r>0?360:-360):r}else v=n,d=e;gc.point(n,e),t(n,e)}function i(){gc.lineStart()}function o(){u(v,d),gc.lineEnd(),aa(y)>Ca&&(l=-(h=180)),M[0]=l,M[1]=h,m=null}function a(n,t){return(t-=n)<0?t+360:t}function c(n,t){return n[0]-t[0]}function s(n,t){return t[0]<=t[1]?t[0]<=n&&n<=t[1]:nhc?(l=-(h=180),f=-(g=90)):y>Ca?g=90:-Ca>y&&(f=-90),M[0]=l,M[1]=h}};return function(n){g=h=-(l=f=1/0),x=[],$o.geo.stream(n,_);var t=x.length;if(t){x.sort(c);for(var e,r=1,u=x[0],i=[u];t>r;++r)e=x[r],s(e[0],u)||s(e[1],u)?(a(u[0],e[1])>a(u[0],u[1])&&(u[1]=e[1]),a(e[0],u[1])>a(u[0],u[1])&&(u[0]=e[0])):i.push(u=e);for(var o,e,p=-1/0,t=i.length-1,r=0,u=i[t];t>=r;u=e,++r)e=i[r],(o=a(u[1],e[0]))>p&&(p=o,l=e[0],h=u[1])}return x=M=null,1/0===l||1/0===f?[[0/0,0/0],[0/0,0/0]]:[[l,f],[h,g]]}}(),$o.geo.centroid=function(n){pc=vc=dc=mc=yc=xc=Mc=_c=bc=wc=Sc=0,$o.geo.stream(n,kc);var t=bc,e=wc,r=Sc,u=t*t+e*e+r*r;return Na>u&&(t=xc,e=Mc,r=_c,Ca>vc&&(t=dc,e=mc,r=yc),u=t*t+e*e+r*r,Na>u)?[0/0,0/0]:[Math.atan2(e,t)*Ta,H(r/Math.sqrt(u))*Ta]};var pc,vc,dc,mc,yc,xc,Mc,_c,bc,wc,Sc,kc={sphere:c,point:Ht,lineStart:Ot,lineEnd:Yt,polygonStart:function(){kc.lineStart=It},polygonEnd:function(){kc.lineStart=Ot}},Ec=Bt(Zt,Qt,te,[-ka,-ka/2]),Ac=1e9;$o.geo.clipExtent=function(){var n,t,e,r,u,i,o={stream:function(n){return u&&(u.valid=!1),u=i(n),u.valid=!0,u},extent:function(a){return arguments.length?(i=ue(n=+a[0][0],t=+a[0][1],e=+a[1][0],r=+a[1][1]),u&&(u.valid=!1,u=null),o):[[n,t],[e,r]]}};return o.extent([[0,0],[960,500]])},($o.geo.conicEqualArea=function(){return oe(ae)}).raw=ae,$o.geo.albers=function(){return $o.geo.conicEqualArea().rotate([96,0]).center([-.6,38.7]).parallels([29.5,45.5]).scale(1070)},$o.geo.albersUsa=function(){function n(n){var i=n[0],o=n[1];return t=null,e(i,o),t||(r(i,o),t)||u(i,o),t}var t,e,r,u,i=$o.geo.albers(),o=$o.geo.conicEqualArea().rotate([154,0]).center([-2,58.5]).parallels([55,65]),a=$o.geo.conicEqualArea().rotate([157,0]).center([-3,19.9]).parallels([8,18]),c={point:function(n,e){t=[n,e]}};return n.invert=function(n){var t=i.scale(),e=i.translate(),r=(n[0]-e[0])/t,u=(n[1]-e[1])/t;return(u>=.12&&.234>u&&r>=-.425&&-.214>r?o:u>=.166&&.234>u&&r>=-.214&&-.115>r?a:i).invert(n)},n.stream=function(n){var t=i.stream(n),e=o.stream(n),r=a.stream(n);return{point:function(n,u){t.point(n,u),e.point(n,u),r.point(n,u)},sphere:function(){t.sphere(),e.sphere(),r.sphere()},lineStart:function(){t.lineStart(),e.lineStart(),r.lineStart()},lineEnd:function(){t.lineEnd(),e.lineEnd(),r.lineEnd()},polygonStart:function(){t.polygonStart(),e.polygonStart(),r.polygonStart()},polygonEnd:function(){t.polygonEnd(),e.polygonEnd(),r.polygonEnd()}}},n.precision=function(t){return arguments.length?(i.precision(t),o.precision(t),a.precision(t),n):i.precision()},n.scale=function(t){return arguments.length?(i.scale(t),o.scale(.35*t),a.scale(t),n.translate(i.translate())):i.scale()},n.translate=function(t){if(!arguments.length)return i.translate();var s=i.scale(),l=+t[0],f=+t[1];return e=i.translate(t).clipExtent([[l-.455*s,f-.238*s],[l+.455*s,f+.238*s]]).stream(c).point,r=o.translate([l-.307*s,f+.201*s]).clipExtent([[l-.425*s+Ca,f+.12*s+Ca],[l-.214*s-Ca,f+.234*s-Ca]]).stream(c).point,u=a.translate([l-.205*s,f+.212*s]).clipExtent([[l-.214*s+Ca,f+.166*s+Ca],[l-.115*s-Ca,f+.234*s-Ca]]).stream(c).point,n},n.scale(1070)};var Cc,Nc,Lc,Tc,qc,zc,Rc={point:c,lineStart:c,lineEnd:c,polygonStart:function(){Nc=0,Rc.lineStart=ce},polygonEnd:function(){Rc.lineStart=Rc.lineEnd=Rc.point=c,Cc+=aa(Nc/2)}},Dc={point:se,lineStart:c,lineEnd:c,polygonStart:c,polygonEnd:c},Pc={point:he,lineStart:ge,lineEnd:pe,polygonStart:function(){Pc.lineStart=ve},polygonEnd:function(){Pc.point=he,Pc.lineStart=ge,Pc.lineEnd=pe}};$o.geo.path=function(){function n(n){return n&&("function"==typeof a&&i.pointRadius(+a.apply(this,arguments)),o&&o.valid||(o=u(i)),$o.geo.stream(n,o)),i.result()}function t(){return o=null,n}var e,r,u,i,o,a=4.5;return n.area=function(n){return Cc=0,$o.geo.stream(n,u(Rc)),Cc},n.centroid=function(n){return dc=mc=yc=xc=Mc=_c=bc=wc=Sc=0,$o.geo.stream(n,u(Pc)),Sc?[bc/Sc,wc/Sc]:_c?[xc/_c,Mc/_c]:yc?[dc/yc,mc/yc]:[0/0,0/0]},n.bounds=function(n){return qc=zc=-(Lc=Tc=1/0),$o.geo.stream(n,u(Dc)),[[Lc,Tc],[qc,zc]]},n.projection=function(n){return arguments.length?(u=(e=n)?n.stream||ye(n):vt,t()):e},n.context=function(n){return arguments.length?(i=null==(r=n)?new le:new de(n),"function"!=typeof a&&i.pointRadius(a),t()):r},n.pointRadius=function(t){return arguments.length?(a="function"==typeof t?t:(i.pointRadius(+t),+t),n):a},n.projection($o.geo.albersUsa()).context(null)},$o.geo.transform=function(n){return{stream:function(t){var e=new xe(t);for(var r in n)e[r]=n[r];return e}}},xe.prototype={point:function(n,t){this.stream.point(n,t)},sphere:function(){this.stream.sphere()},lineStart:function(){this.stream.lineStart() -},lineEnd:function(){this.stream.lineEnd()},polygonStart:function(){this.stream.polygonStart()},polygonEnd:function(){this.stream.polygonEnd()}},$o.geo.projection=_e,$o.geo.projectionMutator=be,($o.geo.equirectangular=function(){return _e(Se)}).raw=Se.invert=Se,$o.geo.rotation=function(n){function t(t){return t=n(t[0]*La,t[1]*La),t[0]*=Ta,t[1]*=Ta,t}return n=Ee(n[0]%360*La,n[1]*La,n.length>2?n[2]*La:0),t.invert=function(t){return t=n.invert(t[0]*La,t[1]*La),t[0]*=Ta,t[1]*=Ta,t},t},ke.invert=Se,$o.geo.circle=function(){function n(){var n="function"==typeof r?r.apply(this,arguments):r,t=Ee(-n[0]*La,-n[1]*La,0).invert,u=[];return e(null,null,1,{point:function(n,e){u.push(n=t(n,e)),n[0]*=Ta,n[1]*=Ta}}),{type:"Polygon",coordinates:[u]}}var t,e,r=[0,0],u=6;return n.origin=function(t){return arguments.length?(r=t,n):r},n.angle=function(r){return arguments.length?(e=Le((t=+r)*La,u*La),n):t},n.precision=function(r){return arguments.length?(e=Le(t*La,(u=+r)*La),n):u},n.angle(90)},$o.geo.distance=function(n,t){var e,r=(t[0]-n[0])*La,u=n[1]*La,i=t[1]*La,o=Math.sin(r),a=Math.cos(r),c=Math.sin(u),s=Math.cos(u),l=Math.sin(i),f=Math.cos(i);return Math.atan2(Math.sqrt((e=f*o)*e+(e=s*l-c*f*a)*e),c*l+s*f*a)},$o.geo.graticule=function(){function n(){return{type:"MultiLineString",coordinates:t()}}function t(){return $o.range(Math.ceil(i/d)*d,u,d).map(h).concat($o.range(Math.ceil(s/m)*m,c,m).map(g)).concat($o.range(Math.ceil(r/p)*p,e,p).filter(function(n){return aa(n%d)>Ca}).map(l)).concat($o.range(Math.ceil(a/v)*v,o,v).filter(function(n){return aa(n%m)>Ca}).map(f))}var e,r,u,i,o,a,c,s,l,f,h,g,p=10,v=p,d=90,m=360,y=2.5;return n.lines=function(){return t().map(function(n){return{type:"LineString",coordinates:n}})},n.outline=function(){return{type:"Polygon",coordinates:[h(i).concat(g(c).slice(1),h(u).reverse().slice(1),g(s).reverse().slice(1))]}},n.extent=function(t){return arguments.length?n.majorExtent(t).minorExtent(t):n.minorExtent()},n.majorExtent=function(t){return arguments.length?(i=+t[0][0],u=+t[1][0],s=+t[0][1],c=+t[1][1],i>u&&(t=i,i=u,u=t),s>c&&(t=s,s=c,c=t),n.precision(y)):[[i,s],[u,c]]},n.minorExtent=function(t){return arguments.length?(r=+t[0][0],e=+t[1][0],a=+t[0][1],o=+t[1][1],r>e&&(t=r,r=e,e=t),a>o&&(t=a,a=o,o=t),n.precision(y)):[[r,a],[e,o]]},n.step=function(t){return arguments.length?n.majorStep(t).minorStep(t):n.minorStep()},n.majorStep=function(t){return arguments.length?(d=+t[0],m=+t[1],n):[d,m]},n.minorStep=function(t){return arguments.length?(p=+t[0],v=+t[1],n):[p,v]},n.precision=function(t){return arguments.length?(y=+t,l=qe(a,o,90),f=ze(r,e,y),h=qe(s,c,90),g=ze(i,u,y),n):y},n.majorExtent([[-180,-90+Ca],[180,90-Ca]]).minorExtent([[-180,-80-Ca],[180,80+Ca]])},$o.geo.greatArc=function(){function n(){return{type:"LineString",coordinates:[t||r.apply(this,arguments),e||u.apply(this,arguments)]}}var t,e,r=Re,u=De;return n.distance=function(){return $o.geo.distance(t||r.apply(this,arguments),e||u.apply(this,arguments))},n.source=function(e){return arguments.length?(r=e,t="function"==typeof e?null:e,n):r},n.target=function(t){return arguments.length?(u=t,e="function"==typeof t?null:t,n):u},n.precision=function(){return arguments.length?n:0},n},$o.geo.interpolate=function(n,t){return Pe(n[0]*La,n[1]*La,t[0]*La,t[1]*La)},$o.geo.length=function(n){return Uc=0,$o.geo.stream(n,jc),Uc};var Uc,jc={sphere:c,point:c,lineStart:Ue,lineEnd:c,polygonStart:c,polygonEnd:c},Hc=je(function(n){return Math.sqrt(2/(1+n))},function(n){return 2*Math.asin(n/2)});($o.geo.azimuthalEqualArea=function(){return _e(Hc)}).raw=Hc;var Fc=je(function(n){var t=Math.acos(n);return t&&t/Math.sin(t)},vt);($o.geo.azimuthalEquidistant=function(){return _e(Fc)}).raw=Fc,($o.geo.conicConformal=function(){return oe(He)}).raw=He,($o.geo.conicEquidistant=function(){return oe(Fe)}).raw=Fe;var Oc=je(function(n){return 1/n},Math.atan);($o.geo.gnomonic=function(){return _e(Oc)}).raw=Oc,Oe.invert=function(n,t){return[n,2*Math.atan(Math.exp(t))-Aa]},($o.geo.mercator=function(){return Ye(Oe)}).raw=Oe;var Yc=je(function(){return 1},Math.asin);($o.geo.orthographic=function(){return _e(Yc)}).raw=Yc;var Ic=je(function(n){return 1/(1+n)},function(n){return 2*Math.atan(n)});($o.geo.stereographic=function(){return _e(Ic)}).raw=Ic,Ie.invert=function(n,t){return[Math.atan2(F(n),Math.cos(t)),H(Math.sin(t)/O(n))]},($o.geo.transverseMercator=function(){return Ye(Ie)}).raw=Ie,$o.geom={},$o.geom.hull=function(n){function t(n){if(n.length<3)return[];var t,u,i,o,a,c,s,l,f,h,g,p,v=pt(e),d=pt(r),m=n.length,y=m-1,x=[],M=[],_=0;if(v===Ze&&r===Ve)t=n;else for(i=0,t=[];m>i;++i)t.push([+v.call(this,u=n[i],i),+d.call(this,u,i)]);for(i=1;m>i;++i)(t[i][1]i;++i)i!==_&&(c=t[i][1]-t[_][1],a=t[i][0]-t[_][0],x.push({angle:Math.atan2(c,a),index:i}));for(x.sort(function(n,t){return n.angle-t.angle}),g=x[0].angle,h=x[0].index,f=0,i=1;y>i;++i){if(o=x[i].index,g==x[i].angle){if(a=t[h][0]-t[_][0],c=t[h][1]-t[_][1],s=t[o][0]-t[_][0],l=t[o][1]-t[_][1],a*a+c*c>=s*s+l*l){x[i].index=-1;continue}x[f].index=-1}g=x[i].angle,f=i,h=o}for(M.push(_),i=0,o=0;2>i;++o)x[o].index>-1&&(M.push(x[o].index),i++);for(p=M.length;y>o;++o)if(!(x[o].index<0)){for(;!Xe(M[p-2],M[p-1],x[o].index,t);)--p;M[p++]=x[o].index}var b=[];for(i=p-1;i>=0;--i)b.push(n[M[i]]);return b}var e=Ze,r=Ve;return arguments.length?t(n):(t.x=function(n){return arguments.length?(e=n,t):e},t.y=function(n){return arguments.length?(r=n,t):r},t)},$o.geom.polygon=function(n){return ha(n,Zc),n};var Zc=$o.geom.polygon.prototype=[];Zc.area=function(){for(var n,t=-1,e=this.length,r=this[e-1],u=0;++t=r&&s.x<=i&&s.y>=u&&s.y<=o?[[r,o],[i,o],[i,u],[r,u]]:[];l.point=n[a]}),t}function e(n){return n.map(function(n,t){return{x:Math.round(i(n,t)/Ca)*Ca,y:Math.round(o(n,t)/Ca)*Ca,i:t}})}var r=Ze,u=Ve,i=r,o=u,a=Kc;return n?t(n):(t.links=function(n){return _r(e(n)).edges.filter(function(n){return n.l&&n.r}).map(function(t){return{source:n[t.l.i],target:n[t.r.i]}})},t.triangles=function(n){var t=[];return _r(e(n)).cells.forEach(function(e,r){for(var u,i,o=e.site,a=e.edges.sort(ir),c=-1,s=a.length,l=a[s-1].edge,f=l.l===o?l.r:l.l;++c=s,h=r>=l,g=(h<<1)+f;n.leaf=!1,n=n.nodes[g]||(n.nodes[g]=Er()),f?u=s:a=s,h?o=l:c=l,i(n,t,e,r,u,o,a,c)}var l,f,h,g,p,v,d,m,y,x=pt(a),M=pt(c);if(null!=t)v=t,d=e,m=r,y=u;else if(m=y=-(v=d=1/0),f=[],h=[],p=n.length,o)for(g=0;p>g;++g)l=n[g],l.xm&&(m=l.x),l.y>y&&(y=l.y),f.push(l.x),h.push(l.y);else for(g=0;p>g;++g){var _=+x(l=n[g],g),b=+M(l,g);v>_&&(v=_),d>b&&(d=b),_>m&&(m=_),b>y&&(y=b),f.push(_),h.push(b)}var w=m-v,S=y-d;w>S?y=d+w:m=v+S;var k=Er();if(k.add=function(n){i(k,n,+x(n,++g),+M(n,g),v,d,m,y)},k.visit=function(n){Ar(n,k,v,d,m,y)},g=-1,null==t){for(;++g=0?n.substring(0,t):n,r=t>=0?n.substring(t+1):"in";return e=ts.get(e)||ns,r=es.get(r)||vt,Rr(r(e.apply(null,Bo.call(arguments,1))))},$o.interpolateHcl=$r,$o.interpolateHsl=Br,$o.interpolateLab=Wr,$o.interpolateRound=Jr,$o.transform=function(n){var t=Jo.createElementNS($o.ns.prefix.svg,"g");return($o.transform=function(n){if(null!=n){t.setAttribute("transform",n);var e=t.transform.baseVal.consolidate()}return new Gr(e?e.matrix:rs)})(n)},Gr.prototype.toString=function(){return"translate("+this.translate+")rotate("+this.rotate+")skewX("+this.skew+")scale("+this.scale+")"};var rs={a:1,b:0,c:0,d:1,e:0,f:0};$o.interpolateTransform=tu,$o.layout={},$o.layout.bundle=function(){return function(n){for(var t=[],e=-1,r=n.length;++e(u-e)*a){var c=t.charge*a*a;return n.px-=i*c,n.py-=o*c,!0}if(t.point&&isFinite(a)){var c=t.pointCharge*a*a;n.px-=i*c,n.py-=o*c}}return!t.charge}}function t(n){n.px=$o.event.x,n.py=$o.event.y,a.resume()}var e,r,u,i,o,a={},c=$o.dispatch("start","tick","end"),s=[1,1],l=.9,f=us,h=is,g=-30,p=.1,v=.8,d=[],m=[];return a.tick=function(){if((r*=.99)<.005)return c.end({type:"end",alpha:r=0}),!0;var t,e,a,f,h,v,y,x,M,_=d.length,b=m.length;for(e=0;b>e;++e)a=m[e],f=a.source,h=a.target,x=h.x-f.x,M=h.y-f.y,(v=x*x+M*M)&&(v=r*i[e]*((v=Math.sqrt(v))-u[e])/v,x*=v,M*=v,h.x-=x*(y=f.weight/(h.weight+f.weight)),h.y-=M*y,f.x+=x*(y=1-y),f.y+=M*y);if((y=r*p)&&(x=s[0]/2,M=s[1]/2,e=-1,y))for(;++e<_;)a=d[e],a.x+=(x-a.x)*y,a.y+=(M-a.y)*y;if(g)for(fu(t=$o.geom.quadtree(d),r,o),e=-1;++e<_;)(a=d[e]).fixed||t.visit(n(a));for(e=-1;++e<_;)a=d[e],a.fixed?(a.x=a.px,a.y=a.py):(a.x-=(a.px-(a.px=a.x))*l,a.y-=(a.py-(a.py=a.y))*l);c.tick({type:"tick",alpha:r})},a.nodes=function(n){return arguments.length?(d=n,a):d},a.links=function(n){return arguments.length?(m=n,a):m},a.size=function(n){return arguments.length?(s=n,a):s},a.linkDistance=function(n){return arguments.length?(f="function"==typeof n?n:+n,a):f},a.distance=a.linkDistance,a.linkStrength=function(n){return arguments.length?(h="function"==typeof n?n:+n,a):h},a.friction=function(n){return arguments.length?(l=+n,a):l},a.charge=function(n){return arguments.length?(g="function"==typeof n?n:+n,a):g},a.gravity=function(n){return arguments.length?(p=+n,a):p},a.theta=function(n){return arguments.length?(v=+n,a):v},a.alpha=function(n){return arguments.length?(n=+n,r?r=n>0?n:0:n>0&&(c.start({type:"start",alpha:r=n}),$o.timer(a.tick)),a):r},a.start=function(){function n(n,r){if(!e){for(e=new Array(c),a=0;c>a;++a)e[a]=[];for(a=0;s>a;++a){var u=m[a];e[u.source.index].push(u.target),e[u.target.index].push(u.source)}}for(var i,o=e[t],a=-1,s=o.length;++at;++t)(r=d[t]).index=t,r.weight=0;for(t=0;l>t;++t)r=m[t],"number"==typeof r.source&&(r.source=d[r.source]),"number"==typeof r.target&&(r.target=d[r.target]),++r.source.weight,++r.target.weight;for(t=0;c>t;++t)r=d[t],isNaN(r.x)&&(r.x=n("x",p)),isNaN(r.y)&&(r.y=n("y",v)),isNaN(r.px)&&(r.px=r.x),isNaN(r.py)&&(r.py=r.y);if(u=[],"function"==typeof f)for(t=0;l>t;++t)u[t]=+f.call(this,m[t],t);else for(t=0;l>t;++t)u[t]=f;if(i=[],"function"==typeof h)for(t=0;l>t;++t)i[t]=+h.call(this,m[t],t);else for(t=0;l>t;++t)i[t]=h;if(o=[],"function"==typeof g)for(t=0;c>t;++t)o[t]=+g.call(this,d[t],t);else for(t=0;c>t;++t)o[t]=g;return a.resume()},a.resume=function(){return a.alpha(.1)},a.stop=function(){return a.alpha(0)},a.drag=function(){return e||(e=$o.behavior.drag().origin(vt).on("dragstart.force",au).on("drag.force",t).on("dragend.force",cu)),arguments.length?(this.on("mouseover.force",su).on("mouseout.force",lu).call(e),void 0):e},$o.rebind(a,c,"on")};var us=20,is=1;$o.layout.hierarchy=function(){function n(t,o,a){var c=u.call(e,t,o);if(t.depth=o,a.push(t),c&&(s=c.length)){for(var s,l,f=-1,h=t.children=new Array(s),g=0,p=o+1;++fg;++g)for(u.call(n,s[0][g],p=v[g],l[0][g][1]),h=1;d>h;++h)u.call(n,s[h][g],p+=l[h-1][g][1],l[h][g][1]);return a}var t=vt,e=Mu,r=_u,u=xu,i=mu,o=yu;return n.values=function(e){return arguments.length?(t=e,n):t},n.order=function(t){return arguments.length?(e="function"==typeof t?t:as.get(t)||Mu,n):e},n.offset=function(t){return arguments.length?(r="function"==typeof t?t:cs.get(t)||_u,n):r},n.x=function(t){return arguments.length?(i=t,n):i},n.y=function(t){return arguments.length?(o=t,n):o},n.out=function(t){return arguments.length?(u=t,n):u},n};var as=$o.map({"inside-out":function(n){var t,e,r=n.length,u=n.map(bu),i=n.map(wu),o=$o.range(r).sort(function(n,t){return u[n]-u[t]}),a=0,c=0,s=[],l=[];for(t=0;r>t;++t)e=o[t],c>a?(a+=i[e],s.push(e)):(c+=i[e],l.push(e));return l.reverse().concat(s)},reverse:function(n){return $o.range(n.length).reverse()},"default":Mu}),cs=$o.map({silhouette:function(n){var t,e,r,u=n.length,i=n[0].length,o=[],a=0,c=[];for(e=0;i>e;++e){for(t=0,r=0;u>t;t++)r+=n[t][e][1];r>a&&(a=r),o.push(r)}for(e=0;i>e;++e)c[e]=(a-o[e])/2;return c},wiggle:function(n){var t,e,r,u,i,o,a,c,s,l=n.length,f=n[0],h=f.length,g=[];for(g[0]=c=s=0,e=1;h>e;++e){for(t=0,u=0;l>t;++t)u+=n[t][e][1];for(t=0,i=0,a=f[e][0]-f[e-1][0];l>t;++t){for(r=0,o=(n[t][e][1]-n[t][e-1][1])/(2*a);t>r;++r)o+=(n[r][e][1]-n[r][e-1][1])/a;i+=o*n[t][e][1]}g[e]=c-=u?i/u*a:0,s>c&&(s=c)}for(e=0;h>e;++e)g[e]-=s;return g},expand:function(n){var t,e,r,u=n.length,i=n[0].length,o=1/u,a=[];for(e=0;i>e;++e){for(t=0,r=0;u>t;t++)r+=n[t][e][1];if(r)for(t=0;u>t;t++)n[t][e][1]/=r;else for(t=0;u>t;t++)n[t][e][1]=o}for(e=0;i>e;++e)a[e]=0;return a},zero:_u});$o.layout.histogram=function(){function n(n,i){for(var o,a,c=[],s=n.map(e,this),l=r.call(this,s,i),f=u.call(this,l,s,i),i=-1,h=s.length,g=f.length-1,p=t?1:1/h;++i0)for(i=-1;++i=l[0]&&a<=l[1]&&(o=c[$o.bisect(f,a,1,g)-1],o.y+=p,o.push(n[i]));return c}var t=!0,e=Number,r=Au,u=ku;return n.value=function(t){return arguments.length?(e=t,n):e},n.range=function(t){return arguments.length?(r=pt(t),n):r},n.bins=function(t){return arguments.length?(u="number"==typeof t?function(n){return Eu(n,t)}:pt(t),n):u},n.frequency=function(e){return arguments.length?(t=!!e,n):t},n},$o.layout.tree=function(){function n(n,i){function o(n,t){var r=n.children,u=n._tree;if(r&&(i=r.length)){for(var i,a,s,l=r[0],f=l,h=-1;++h0&&(Uu(ju(a,n,r),n,u),s+=u,l+=u),f+=a._tree.mod,s+=i._tree.mod,h+=c._tree.mod,l+=o._tree.mod;a&&!Lu(o)&&(o._tree.thread=a,o._tree.mod+=f-l),i&&!Nu(c)&&(c._tree.thread=i,c._tree.mod+=s-h,r=n)}return r}var s=t.call(this,n,i),l=s[0];Du(l,function(n,t){n._tree={ancestor:n,prelim:0,mod:0,change:0,shift:0,number:t?t._tree.number+1:0}}),o(l),a(l,-l._tree.prelim);var f=Tu(l,zu),h=Tu(l,qu),g=Tu(l,Ru),p=f.x-e(f,h)/2,v=h.x+e(h,f)/2,d=g.depth||1;return Du(l,u?function(n){n.x*=r[0],n.y=n.depth*r[1],delete n._tree}:function(n){n.x=(n.x-p)/(v-p)*r[0],n.y=n.depth/d*r[1],delete n._tree}),s}var t=$o.layout.hierarchy().sort(null).value(null),e=Cu,r=[1,1],u=!1;return n.separation=function(t){return arguments.length?(e=t,n):e},n.size=function(t){return arguments.length?(u=null==(r=t),n):u?null:r},n.nodeSize=function(t){return arguments.length?(u=null!=(r=t),n):u?r:null},hu(n,t)},$o.layout.pack=function(){function n(n,i){var o=e.call(this,n,i),a=o[0],c=u[0],s=u[1],l=null==t?Math.sqrt:"function"==typeof t?t:function(){return t};if(a.x=a.y=0,Du(a,function(n){n.r=+l(n.value)}),Du(a,Iu),r){var f=r*(t?1:Math.max(2*a.r/c,2*a.r/s))/2;Du(a,function(n){n.r+=f}),Du(a,Iu),Du(a,function(n){n.r-=f})}return Xu(a,c/2,s/2,t?1:1/Math.max(2*a.r/c,2*a.r/s)),o}var t,e=$o.layout.hierarchy().sort(Hu),r=0,u=[1,1];return n.size=function(t){return arguments.length?(u=t,n):u},n.radius=function(e){return arguments.length?(t=null==e||"function"==typeof e?e:+e,n):t},n.padding=function(t){return arguments.length?(r=+t,n):r},hu(n,e)},$o.layout.cluster=function(){function n(n,i){var o,a=t.call(this,n,i),c=a[0],s=0;Du(c,function(n){var t=n.children;t&&t.length?(n.x=Wu(t),n.y=Bu(t)):(n.x=o?s+=e(n,o):0,n.y=0,o=n)});var l=Ju(c),f=Gu(c),h=l.x-e(l,f)/2,g=f.x+e(f,l)/2;return Du(c,u?function(n){n.x=(n.x-c.x)*r[0],n.y=(c.y-n.y)*r[1]}:function(n){n.x=(n.x-h)/(g-h)*r[0],n.y=(1-(c.y?n.y/c.y:1))*r[1]}),a}var t=$o.layout.hierarchy().sort(null).value(null),e=Cu,r=[1,1],u=!1;return n.separation=function(t){return arguments.length?(e=t,n):e},n.size=function(t){return arguments.length?(u=null==(r=t),n):u?null:r},n.nodeSize=function(t){return arguments.length?(u=null!=(r=t),n):u?r:null},hu(n,t)},$o.layout.treemap=function(){function n(n,t){for(var e,r,u=-1,i=n.length;++ut?0:t),e.area=isNaN(r)||0>=r?0:r}function t(e){var i=e.children;if(i&&i.length){var o,a,c,s=f(e),l=[],h=i.slice(),p=1/0,v="slice"===g?s.dx:"dice"===g?s.dy:"slice-dice"===g?1&e.depth?s.dy:s.dx:Math.min(s.dx,s.dy);for(n(h,s.dx*s.dy/e.value),l.area=0;(c=h.length)>0;)l.push(o=h[c-1]),l.area+=o.area,"squarify"!==g||(a=r(l,v))<=p?(h.pop(),p=a):(l.area-=l.pop().area,u(l,v,s,!1),v=Math.min(s.dx,s.dy),l.length=l.area=0,p=1/0);l.length&&(u(l,v,s,!0),l.length=l.area=0),i.forEach(t)}}function e(t){var r=t.children;if(r&&r.length){var i,o=f(t),a=r.slice(),c=[];for(n(a,o.dx*o.dy/t.value),c.area=0;i=a.pop();)c.push(i),c.area+=i.area,null!=i.z&&(u(c,i.z?o.dx:o.dy,o,!a.length),c.length=c.area=0);r.forEach(e)}}function r(n,t){for(var e,r=n.area,u=0,i=1/0,o=-1,a=n.length;++oe&&(i=e),e>u&&(u=e));return r*=r,t*=t,r?Math.max(t*u*p/r,r/(t*i*p)):1/0}function u(n,t,e,r){var u,i=-1,o=n.length,a=e.x,s=e.y,l=t?c(n.area/t):0;if(t==e.dx){for((r||l>e.dy)&&(l=e.dy);++ie.dx)&&(l=e.dx);++ie&&(t=1),1>e&&(n=0),function(){var e,r,u;do e=2*Math.random()-1,r=2*Math.random()-1,u=e*e+r*r;while(!u||u>1);return n+t*e*Math.sqrt(-2*Math.log(u)/u)}},logNormal:function(){var n=$o.random.normal.apply($o,arguments);return function(){return Math.exp(n())}},irwinHall:function(n){return function(){for(var t=0,e=0;n>e;e++)t+=Math.random();return t/n}}},$o.scale={};var ss={floor:vt,ceil:vt};$o.scale.linear=function(){return oi([0,1],[0,1],qr,!1)};var ls={s:1,g:1,p:1,r:1,e:1};$o.scale.log=function(){return pi($o.scale.linear().domain([0,1]),10,!0,[1,10])};var fs=$o.format(".0e"),hs={floor:function(n){return-Math.ceil(-n)},ceil:function(n){return-Math.floor(-n)}};$o.scale.pow=function(){return vi($o.scale.linear(),1,[0,1])},$o.scale.sqrt=function(){return $o.scale.pow().exponent(.5)},$o.scale.ordinal=function(){return mi([],{t:"range",a:[[]]})},$o.scale.category10=function(){return $o.scale.ordinal().range(gs)},$o.scale.category20=function(){return $o.scale.ordinal().range(ps)},$o.scale.category20b=function(){return $o.scale.ordinal().range(vs)},$o.scale.category20c=function(){return $o.scale.ordinal().range(ds)};var gs=[2062260,16744206,2924588,14034728,9725885,9197131,14907330,8355711,12369186,1556175].map(it),ps=[2062260,11454440,16744206,16759672,2924588,10018698,14034728,16750742,9725885,12955861,9197131,12885140,14907330,16234194,8355711,13092807,12369186,14408589,1556175,10410725].map(it),vs=[3750777,5395619,7040719,10264286,6519097,9216594,11915115,13556636,9202993,12426809,15186514,15190932,8666169,11356490,14049643,15177372,8077683,10834324,13528509,14589654].map(it),ds=[3244733,7057110,10406625,13032431,15095053,16616764,16625259,16634018,3253076,7652470,10607003,13101504,7695281,10394312,12369372,14342891,6513507,9868950,12434877,14277081].map(it);$o.scale.quantile=function(){return yi([],[])},$o.scale.quantize=function(){return xi(0,1,[0,1])},$o.scale.threshold=function(){return Mi([.5],[0,1])},$o.scale.identity=function(){return _i([0,1])},$o.svg={},$o.svg.arc=function(){function n(){var n=t.apply(this,arguments),i=e.apply(this,arguments),o=r.apply(this,arguments)+ms,a=u.apply(this,arguments)+ms,c=(o>a&&(c=o,o=a,a=c),a-o),s=ka>c?"0":"1",l=Math.cos(o),f=Math.sin(o),h=Math.cos(a),g=Math.sin(a);return c>=ys?n?"M0,"+i+"A"+i+","+i+" 0 1,1 0,"+-i+"A"+i+","+i+" 0 1,1 0,"+i+"M0,"+n+"A"+n+","+n+" 0 1,0 0,"+-n+"A"+n+","+n+" 0 1,0 0,"+n+"Z":"M0,"+i+"A"+i+","+i+" 0 1,1 0,"+-i+"A"+i+","+i+" 0 1,1 0,"+i+"Z":n?"M"+i*l+","+i*f+"A"+i+","+i+" 0 "+s+",1 "+i*h+","+i*g+"L"+n*h+","+n*g+"A"+n+","+n+" 0 "+s+",0 "+n*l+","+n*f+"Z":"M"+i*l+","+i*f+"A"+i+","+i+" 0 "+s+",1 "+i*h+","+i*g+"L0,0"+"Z"}var t=bi,e=wi,r=Si,u=ki;return n.innerRadius=function(e){return arguments.length?(t=pt(e),n):t},n.outerRadius=function(t){return arguments.length?(e=pt(t),n):e},n.startAngle=function(t){return arguments.length?(r=pt(t),n):r},n.endAngle=function(t){return arguments.length?(u=pt(t),n):u},n.centroid=function(){var n=(t.apply(this,arguments)+e.apply(this,arguments))/2,i=(r.apply(this,arguments)+u.apply(this,arguments))/2+ms;return[Math.cos(i)*n,Math.sin(i)*n]},n};var ms=-Aa,ys=Ea-Ca;$o.svg.line=function(){return Ei(vt)};var xs=$o.map({linear:Ai,"linear-closed":Ci,step:Ni,"step-before":Li,"step-after":Ti,basis:Ui,"basis-open":ji,"basis-closed":Hi,bundle:Fi,cardinal:Ri,"cardinal-open":qi,"cardinal-closed":zi,monotone:Xi});xs.forEach(function(n,t){t.key=n,t.closed=/-closed$/.test(n)});var Ms=[0,2/3,1/3,0],_s=[0,1/3,2/3,0],bs=[0,1/6,2/3,1/6];$o.svg.line.radial=function(){var n=Ei($i);return n.radius=n.x,delete n.x,n.angle=n.y,delete n.y,n},Li.reverse=Ti,Ti.reverse=Li,$o.svg.area=function(){return Bi(vt)},$o.svg.area.radial=function(){var n=Bi($i);return n.radius=n.x,delete n.x,n.innerRadius=n.x0,delete n.x0,n.outerRadius=n.x1,delete n.x1,n.angle=n.y,delete n.y,n.startAngle=n.y0,delete n.y0,n.endAngle=n.y1,delete n.y1,n},$o.svg.chord=function(){function n(n,a){var c=t(this,i,n,a),s=t(this,o,n,a);return"M"+c.p0+r(c.r,c.p1,c.a1-c.a0)+(e(c,s)?u(c.r,c.p1,c.r,c.p0):u(c.r,c.p1,s.r,s.p0)+r(s.r,s.p1,s.a1-s.a0)+u(s.r,s.p1,c.r,c.p0))+"Z"}function t(n,t,e,r){var u=t.call(n,e,r),i=a.call(n,u,r),o=c.call(n,u,r)+ms,l=s.call(n,u,r)+ms;return{r:i,a0:o,a1:l,p0:[i*Math.cos(o),i*Math.sin(o)],p1:[i*Math.cos(l),i*Math.sin(l)]}}function e(n,t){return n.a0==t.a0&&n.a1==t.a1}function r(n,t,e){return"A"+n+","+n+" 0 "+ +(e>ka)+",1 "+t}function u(n,t,e,r){return"Q 0,0 "+r}var i=Re,o=De,a=Wi,c=Si,s=ki;return n.radius=function(t){return arguments.length?(a=pt(t),n):a},n.source=function(t){return arguments.length?(i=pt(t),n):i},n.target=function(t){return arguments.length?(o=pt(t),n):o},n.startAngle=function(t){return arguments.length?(c=pt(t),n):c},n.endAngle=function(t){return arguments.length?(s=pt(t),n):s},n},$o.svg.diagonal=function(){function n(n,u){var i=t.call(this,n,u),o=e.call(this,n,u),a=(i.y+o.y)/2,c=[i,{x:i.x,y:a},{x:o.x,y:a},o];return c=c.map(r),"M"+c[0]+"C"+c[1]+" "+c[2]+" "+c[3]}var t=Re,e=De,r=Ji;return n.source=function(e){return arguments.length?(t=pt(e),n):t},n.target=function(t){return arguments.length?(e=pt(t),n):e},n.projection=function(t){return arguments.length?(r=t,n):r},n},$o.svg.diagonal.radial=function(){var n=$o.svg.diagonal(),t=Ji,e=n.projection;return n.projection=function(n){return arguments.length?e(Gi(t=n)):t},n},$o.svg.symbol=function(){function n(n,r){return(ws.get(t.call(this,n,r))||no)(e.call(this,n,r))}var t=Qi,e=Ki;return n.type=function(e){return arguments.length?(t=pt(e),n):t},n.size=function(t){return arguments.length?(e=pt(t),n):e},n};var ws=$o.map({circle:no,cross:function(n){var t=Math.sqrt(n/5)/2;return"M"+-3*t+","+-t+"H"+-t+"V"+-3*t+"H"+t+"V"+-t+"H"+3*t+"V"+t+"H"+t+"V"+3*t+"H"+-t+"V"+t+"H"+-3*t+"Z"},diamond:function(n){var t=Math.sqrt(n/(2*As)),e=t*As;return"M0,"+-t+"L"+e+",0"+" 0,"+t+" "+-e+",0"+"Z"},square:function(n){var t=Math.sqrt(n)/2;return"M"+-t+","+-t+"L"+t+","+-t+" "+t+","+t+" "+-t+","+t+"Z"},"triangle-down":function(n){var t=Math.sqrt(n/Es),e=t*Es/2;return"M0,"+e+"L"+t+","+-e+" "+-t+","+-e+"Z"},"triangle-up":function(n){var t=Math.sqrt(n/Es),e=t*Es/2;return"M0,"+-e+"L"+t+","+e+" "+-t+","+e+"Z"}});$o.svg.symbolTypes=ws.keys();var Ss,ks,Es=Math.sqrt(3),As=Math.tan(30*La),Cs=[],Ns=0; -Cs.call=ma.call,Cs.empty=ma.empty,Cs.node=ma.node,Cs.size=ma.size,$o.transition=function(n){return arguments.length?Ss?n.transition():n:Ma.transition()},$o.transition.prototype=Cs,Cs.select=function(n){var t,e,r,u=this.id,i=[];n=v(n);for(var o=-1,a=this.length;++oi;i++){u.push(t=[]);for(var e=this[i],a=0,c=e.length;c>a;a++)(r=e[a])&&n.call(r,r.__data__,a)&&t.push(r)}return to(u,this.id)},Cs.tween=function(n,t){var e=this.id;return arguments.length<2?this.node().__transition__[e].tween.get(n):C(this,null==t?function(t){t.__transition__[e].tween.remove(n)}:function(r){r.__transition__[e].tween.set(n,t)})},Cs.attr=function(n,t){function e(){this.removeAttribute(a)}function r(){this.removeAttributeNS(a.space,a.local)}function u(n){return null==n?e:(n+="",function(){var t,e=this.getAttribute(a);return e!==n&&(t=o(e,n),function(n){this.setAttribute(a,t(n))})})}function i(n){return null==n?r:(n+="",function(){var t,e=this.getAttributeNS(a.space,a.local);return e!==n&&(t=o(e,n),function(n){this.setAttributeNS(a.space,a.local,t(n))})})}if(arguments.length<2){for(t in n)this.attr(t,n[t]);return this}var o="transform"==n?tu:qr,a=$o.ns.qualify(n);return eo(this,"attr."+n,t,a.local?i:u)},Cs.attrTween=function(n,t){function e(n,e){var r=t.call(this,n,e,this.getAttribute(u));return r&&function(n){this.setAttribute(u,r(n))}}function r(n,e){var r=t.call(this,n,e,this.getAttributeNS(u.space,u.local));return r&&function(n){this.setAttributeNS(u.space,u.local,r(n))}}var u=$o.ns.qualify(n);return this.tween("attr."+n,u.local?r:e)},Cs.style=function(n,t,e){function r(){this.style.removeProperty(n)}function u(t){return null==t?r:(t+="",function(){var r,u=Ko.getComputedStyle(this,null).getPropertyValue(n);return u!==t&&(r=qr(u,t),function(t){this.style.setProperty(n,r(t),e)})})}var i=arguments.length;if(3>i){if("string"!=typeof n){2>i&&(t="");for(e in n)this.style(e,n[e],t);return this}e=""}return eo(this,"style."+n,t,u)},Cs.styleTween=function(n,t,e){function r(r,u){var i=t.call(this,r,u,Ko.getComputedStyle(this,null).getPropertyValue(n));return i&&function(t){this.style.setProperty(n,i(t),e)}}return arguments.length<3&&(e=""),this.tween("style."+n,r)},Cs.text=function(n){return eo(this,"text",n,ro)},Cs.remove=function(){return this.each("end.transition",function(){var n;this.__transition__.count<2&&(n=this.parentNode)&&n.removeChild(this)})},Cs.ease=function(n){var t=this.id;return arguments.length<1?this.node().__transition__[t].ease:("function"!=typeof n&&(n=$o.ease.apply($o,arguments)),C(this,function(e){e.__transition__[t].ease=n}))},Cs.delay=function(n){var t=this.id;return C(this,"function"==typeof n?function(e,r,u){e.__transition__[t].delay=+n.call(e,e.__data__,r,u)}:(n=+n,function(e){e.__transition__[t].delay=n}))},Cs.duration=function(n){var t=this.id;return C(this,"function"==typeof n?function(e,r,u){e.__transition__[t].duration=Math.max(1,n.call(e,e.__data__,r,u))}:(n=Math.max(1,n),function(e){e.__transition__[t].duration=n}))},Cs.each=function(n,t){var e=this.id;if(arguments.length<2){var r=ks,u=Ss;Ss=e,C(this,function(t,r,u){ks=t.__transition__[e],n.call(t,t.__data__,r,u)}),ks=r,Ss=u}else C(this,function(r){var u=r.__transition__[e];(u.event||(u.event=$o.dispatch("start","end"))).on(n,t)});return this},Cs.transition=function(){for(var n,t,e,r,u=this.id,i=++Ns,o=[],a=0,c=this.length;c>a;a++){o.push(n=[]);for(var t=this[a],s=0,l=t.length;l>s;s++)(e=t[s])&&(r=Object.create(e.__transition__[u]),r.delay+=r.duration,uo(e,s,i,r)),n.push(e)}return to(o,i)},$o.svg.axis=function(){function n(n){n.each(function(){var n,s=$o.select(this),l=this.__chart__||e,f=this.__chart__=e.copy(),h=null==c?f.ticks?f.ticks.apply(f,a):f.domain():c,g=null==t?f.tickFormat?f.tickFormat.apply(f,a):vt:t,p=s.selectAll(".tick").data(h,f),v=p.enter().insert("g",".domain").attr("class","tick").style("opacity",Ca),d=$o.transition(p.exit()).style("opacity",Ca).remove(),m=$o.transition(p).style("opacity",1),y=ti(f),x=s.selectAll(".domain").data([0]),M=(x.enter().append("path").attr("class","domain"),$o.transition(x));v.append("line"),v.append("text");var _=v.select("line"),b=m.select("line"),w=p.select("text").text(g),S=v.select("text"),k=m.select("text");switch(r){case"bottom":n=io,_.attr("y2",u),S.attr("y",Math.max(u,0)+o),b.attr("x2",0).attr("y2",u),k.attr("x",0).attr("y",Math.max(u,0)+o),w.attr("dy",".71em").style("text-anchor","middle"),M.attr("d","M"+y[0]+","+i+"V0H"+y[1]+"V"+i);break;case"top":n=io,_.attr("y2",-u),S.attr("y",-(Math.max(u,0)+o)),b.attr("x2",0).attr("y2",-u),k.attr("x",0).attr("y",-(Math.max(u,0)+o)),w.attr("dy","0em").style("text-anchor","middle"),M.attr("d","M"+y[0]+","+-i+"V0H"+y[1]+"V"+-i);break;case"left":n=oo,_.attr("x2",-u),S.attr("x",-(Math.max(u,0)+o)),b.attr("x2",-u).attr("y2",0),k.attr("x",-(Math.max(u,0)+o)).attr("y",0),w.attr("dy",".32em").style("text-anchor","end"),M.attr("d","M"+-i+","+y[0]+"H0V"+y[1]+"H"+-i);break;case"right":n=oo,_.attr("x2",u),S.attr("x",Math.max(u,0)+o),b.attr("x2",u).attr("y2",0),k.attr("x",Math.max(u,0)+o).attr("y",0),w.attr("dy",".32em").style("text-anchor","start"),M.attr("d","M"+i+","+y[0]+"H0V"+y[1]+"H"+i)}if(f.rangeBand){var E=f.rangeBand()/2,A=function(n){return f(n)+E};v.call(n,A),m.call(n,A)}else v.call(n,l),m.call(n,f),d.call(n,f)})}var t,e=$o.scale.linear(),r=Ls,u=6,i=6,o=3,a=[10],c=null;return n.scale=function(t){return arguments.length?(e=t,n):e},n.orient=function(t){return arguments.length?(r=t in Ts?t+"":Ls,n):r},n.ticks=function(){return arguments.length?(a=arguments,n):a},n.tickValues=function(t){return arguments.length?(c=t,n):c},n.tickFormat=function(e){return arguments.length?(t=e,n):t},n.tickSize=function(t){var e=arguments.length;return e?(u=+t,i=+arguments[e-1],n):u},n.innerTickSize=function(t){return arguments.length?(u=+t,n):u},n.outerTickSize=function(t){return arguments.length?(i=+t,n):i},n.tickPadding=function(t){return arguments.length?(o=+t,n):o},n.tickSubdivide=function(){return arguments.length&&n},n};var Ls="bottom",Ts={top:1,right:1,bottom:1,left:1};$o.svg.brush=function(){function n(i){i.each(function(){var i=$o.select(this).style("pointer-events","all").style("-webkit-tap-highlight-color","rgba(0,0,0,0)").on("mousedown.brush",u).on("touchstart.brush",u),o=i.selectAll(".background").data([0]);o.enter().append("rect").attr("class","background").style("visibility","hidden").style("cursor","crosshair"),i.selectAll(".extent").data([0]).enter().append("rect").attr("class","extent").style("cursor","move");var a=i.selectAll(".resize").data(d,vt);a.exit().remove(),a.enter().append("g").attr("class",function(n){return"resize "+n}).style("cursor",function(n){return qs[n]}).append("rect").attr("x",function(n){return/[ew]$/.test(n)?-3:null}).attr("y",function(n){return/^[ns]/.test(n)?-3:null}).attr("width",6).attr("height",6).style("visibility","hidden"),a.style("display",n.empty()?"none":null);var l,f=$o.transition(i),h=$o.transition(o);c&&(l=ti(c),h.attr("x",l[0]).attr("width",l[1]-l[0]),e(f)),s&&(l=ti(s),h.attr("y",l[0]).attr("height",l[1]-l[0]),r(f)),t(f)})}function t(n){n.selectAll(".resize").attr("transform",function(n){return"translate("+l[+/e$/.test(n)]+","+h[+/^s/.test(n)]+")"})}function e(n){n.select(".extent").attr("x",l[0]),n.selectAll(".extent,.n>rect,.s>rect").attr("width",l[1]-l[0])}function r(n){n.select(".extent").attr("y",h[0]),n.selectAll(".extent,.e>rect,.w>rect").attr("height",h[1]-h[0])}function u(){function u(){32==$o.event.keyCode&&(C||(x=null,L[0]-=l[1],L[1]-=h[1],C=2),f())}function g(){32==$o.event.keyCode&&2==C&&(L[0]+=l[1],L[1]+=h[1],C=0,f())}function d(){var n=$o.mouse(_),u=!1;M&&(n[0]+=M[0],n[1]+=M[1]),C||($o.event.altKey?(x||(x=[(l[0]+l[1])/2,(h[0]+h[1])/2]),L[0]=l[+(n[0]f?(u=r,r=f):u=f),g[0]!=r||g[1]!=u?(e?o=null:i=null,g[0]=r,g[1]=u,!0):void 0}function y(){d(),S.style("pointer-events","all").selectAll(".resize").style("display",n.empty()?"none":null),$o.select("body").style("cursor",null),T.on("mousemove.brush",null).on("mouseup.brush",null).on("touchmove.brush",null).on("touchend.brush",null).on("keydown.brush",null).on("keyup.brush",null),N(),w({type:"brushend"})}var x,M,_=this,b=$o.select($o.event.target),w=a.of(_,arguments),S=$o.select(_),k=b.datum(),E=!/^(n|s)$/.test(k)&&c,A=!/^(e|w)$/.test(k)&&s,C=b.classed("extent"),N=D(),L=$o.mouse(_),T=$o.select(Ko).on("keydown.brush",u).on("keyup.brush",g);if($o.event.changedTouches?T.on("touchmove.brush",d).on("touchend.brush",y):T.on("mousemove.brush",d).on("mouseup.brush",y),S.interrupt().selectAll("*").interrupt(),C)L[0]=l[0]-L[0],L[1]=h[0]-L[1];else if(k){var q=+/w$/.test(k),z=+/^n/.test(k);M=[l[1-q]-L[0],h[1-z]-L[1]],L[0]=l[q],L[1]=h[z]}else $o.event.altKey&&(x=L.slice());S.style("pointer-events","none").selectAll(".resize").style("display",null),$o.select("body").style("cursor",b.style("cursor")),w({type:"brushstart"}),d()}var i,o,a=g(n,"brushstart","brush","brushend"),c=null,s=null,l=[0,0],h=[0,0],p=!0,v=!0,d=zs[0];return n.event=function(n){n.each(function(){var n=a.of(this,arguments),t={x:l,y:h,i:i,j:o},e=this.__chart__||t;this.__chart__=t,Ss?$o.select(this).transition().each("start.brush",function(){i=e.i,o=e.j,l=e.x,h=e.y,n({type:"brushstart"})}).tween("brush:brush",function(){var e=zr(l,t.x),r=zr(h,t.y);return i=o=null,function(u){l=t.x=e(u),h=t.y=r(u),n({type:"brush",mode:"resize"})}}).each("end.brush",function(){i=t.i,o=t.j,n({type:"brush",mode:"resize"}),n({type:"brushend"})}):(n({type:"brushstart"}),n({type:"brush",mode:"resize"}),n({type:"brushend"}))})},n.x=function(t){return arguments.length?(c=t,d=zs[!c<<1|!s],n):c},n.y=function(t){return arguments.length?(s=t,d=zs[!c<<1|!s],n):s},n.clamp=function(t){return arguments.length?(c&&s?(p=!!t[0],v=!!t[1]):c?p=!!t:s&&(v=!!t),n):c&&s?[p,v]:c?p:s?v:null},n.extent=function(t){var e,r,u,a,f;return arguments.length?(c&&(e=t[0],r=t[1],s&&(e=e[0],r=r[0]),i=[e,r],c.invert&&(e=c(e),r=c(r)),e>r&&(f=e,e=r,r=f),(e!=l[0]||r!=l[1])&&(l=[e,r])),s&&(u=t[0],a=t[1],c&&(u=u[1],a=a[1]),o=[u,a],s.invert&&(u=s(u),a=s(a)),u>a&&(f=u,u=a,a=f),(u!=h[0]||a!=h[1])&&(h=[u,a])),n):(c&&(i?(e=i[0],r=i[1]):(e=l[0],r=l[1],c.invert&&(e=c.invert(e),r=c.invert(r)),e>r&&(f=e,e=r,r=f))),s&&(o?(u=o[0],a=o[1]):(u=h[0],a=h[1],s.invert&&(u=s.invert(u),a=s.invert(a)),u>a&&(f=u,u=a,a=f))),c&&s?[[e,u],[r,a]]:c?[e,r]:s&&[u,a])},n.clear=function(){return n.empty()||(l=[0,0],h=[0,0],i=o=null),n},n.empty=function(){return!!c&&l[0]==l[1]||!!s&&h[0]==h[1]},$o.rebind(n,a,"on")};var qs={n:"ns-resize",e:"ew-resize",s:"ns-resize",w:"ew-resize",nw:"nwse-resize",ne:"nesw-resize",se:"nwse-resize",sw:"nesw-resize"},zs=[["n","e","s","w","nw","ne","se","sw"],["e","w"],["n","s"],[]],Rs=$o.time={},Ds=Date,Ps=["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"];ao.prototype={getDate:function(){return this._.getUTCDate()},getDay:function(){return this._.getUTCDay()},getFullYear:function(){return this._.getUTCFullYear()},getHours:function(){return this._.getUTCHours()},getMilliseconds:function(){return this._.getUTCMilliseconds()},getMinutes:function(){return this._.getUTCMinutes()},getMonth:function(){return this._.getUTCMonth()},getSeconds:function(){return this._.getUTCSeconds()},getTime:function(){return this._.getTime()},getTimezoneOffset:function(){return 0},valueOf:function(){return this._.valueOf()},setDate:function(){Us.setUTCDate.apply(this._,arguments)},setDay:function(){Us.setUTCDay.apply(this._,arguments)},setFullYear:function(){Us.setUTCFullYear.apply(this._,arguments)},setHours:function(){Us.setUTCHours.apply(this._,arguments)},setMilliseconds:function(){Us.setUTCMilliseconds.apply(this._,arguments)},setMinutes:function(){Us.setUTCMinutes.apply(this._,arguments)},setMonth:function(){Us.setUTCMonth.apply(this._,arguments)},setSeconds:function(){Us.setUTCSeconds.apply(this._,arguments)},setTime:function(){Us.setTime.apply(this._,arguments)}};var Us=Date.prototype,js="%a %b %e %X %Y",Hs="%m/%d/%Y",Fs="%H:%M:%S",Os=["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],Ys=["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],Is=["January","February","March","April","May","June","July","August","September","October","November","December"],Zs=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"];Rs.year=co(function(n){return n=Rs.day(n),n.setMonth(0,1),n},function(n,t){n.setFullYear(n.getFullYear()+t)},function(n){return n.getFullYear()}),Rs.years=Rs.year.range,Rs.years.utc=Rs.year.utc.range,Rs.day=co(function(n){var t=new Ds(2e3,0);return t.setFullYear(n.getFullYear(),n.getMonth(),n.getDate()),t},function(n,t){n.setDate(n.getDate()+t)},function(n){return n.getDate()-1}),Rs.days=Rs.day.range,Rs.days.utc=Rs.day.utc.range,Rs.dayOfYear=function(n){var t=Rs.year(n);return Math.floor((n-t-6e4*(n.getTimezoneOffset()-t.getTimezoneOffset()))/864e5)},Ps.forEach(function(n,t){n=n.toLowerCase(),t=7-t;var e=Rs[n]=co(function(n){return(n=Rs.day(n)).setDate(n.getDate()-(n.getDay()+t)%7),n},function(n,t){n.setDate(n.getDate()+7*Math.floor(t))},function(n){var e=Rs.year(n).getDay();return Math.floor((Rs.dayOfYear(n)+(e+t)%7)/7)-(e!==t)});Rs[n+"s"]=e.range,Rs[n+"s"].utc=e.utc.range,Rs[n+"OfYear"]=function(n){var e=Rs.year(n).getDay();return Math.floor((Rs.dayOfYear(n)+(e+t)%7)/7)}}),Rs.week=Rs.sunday,Rs.weeks=Rs.sunday.range,Rs.weeks.utc=Rs.sunday.utc.range,Rs.weekOfYear=Rs.sundayOfYear,Rs.format=lo;var Vs=ho(Os),Xs=go(Os),$s=ho(Ys),Bs=go(Ys),Ws=ho(Is),Js=go(Is),Gs=ho(Zs),Ks=go(Zs),Qs=/^%/,nl={"-":"",_:" ",0:"0"},tl={a:function(n){return Ys[n.getDay()]},A:function(n){return Os[n.getDay()]},b:function(n){return Zs[n.getMonth()]},B:function(n){return Is[n.getMonth()]},c:lo(js),d:function(n,t){return po(n.getDate(),t,2)},e:function(n,t){return po(n.getDate(),t,2)},H:function(n,t){return po(n.getHours(),t,2)},I:function(n,t){return po(n.getHours()%12||12,t,2)},j:function(n,t){return po(1+Rs.dayOfYear(n),t,3)},L:function(n,t){return po(n.getMilliseconds(),t,3)},m:function(n,t){return po(n.getMonth()+1,t,2)},M:function(n,t){return po(n.getMinutes(),t,2)},p:function(n){return n.getHours()>=12?"PM":"AM"},S:function(n,t){return po(n.getSeconds(),t,2)},U:function(n,t){return po(Rs.sundayOfYear(n),t,2)},w:function(n){return n.getDay()},W:function(n,t){return po(Rs.mondayOfYear(n),t,2)},x:lo(Hs),X:lo(Fs),y:function(n,t){return po(n.getFullYear()%100,t,2)},Y:function(n,t){return po(n.getFullYear()%1e4,t,4)},Z:jo,"%":function(){return"%"}},el={a:vo,A:mo,b:_o,B:bo,c:wo,d:To,e:To,H:zo,I:zo,j:qo,L:Po,m:Lo,M:Ro,p:Uo,S:Do,U:xo,w:yo,W:Mo,x:So,X:ko,y:Ao,Y:Eo,Z:Co,"%":Ho},rl=/^\s*\d+/,ul=$o.map({am:0,pm:1});lo.utc=Fo;var il=Fo("%Y-%m-%dT%H:%M:%S.%LZ");lo.iso=Date.prototype.toISOString&&+new Date("2000-01-01T00:00:00.000Z")?Oo:il,Oo.parse=function(n){var t=new Date(n);return isNaN(t)?null:t},Oo.toString=il.toString,Rs.second=co(function(n){return new Ds(1e3*Math.floor(n/1e3))},function(n,t){n.setTime(n.getTime()+1e3*Math.floor(t))},function(n){return n.getSeconds()}),Rs.seconds=Rs.second.range,Rs.seconds.utc=Rs.second.utc.range,Rs.minute=co(function(n){return new Ds(6e4*Math.floor(n/6e4))},function(n,t){n.setTime(n.getTime()+6e4*Math.floor(t))},function(n){return n.getMinutes()}),Rs.minutes=Rs.minute.range,Rs.minutes.utc=Rs.minute.utc.range,Rs.hour=co(function(n){var t=n.getTimezoneOffset()/60;return new Ds(36e5*(Math.floor(n/36e5-t)+t))},function(n,t){n.setTime(n.getTime()+36e5*Math.floor(t))},function(n){return n.getHours()}),Rs.hours=Rs.hour.range,Rs.hours.utc=Rs.hour.utc.range,Rs.month=co(function(n){return n=Rs.day(n),n.setDate(1),n},function(n,t){n.setMonth(n.getMonth()+t)},function(n){return n.getMonth()}),Rs.months=Rs.month.range,Rs.months.utc=Rs.month.utc.range;var ol=[1e3,5e3,15e3,3e4,6e4,3e5,9e5,18e5,36e5,108e5,216e5,432e5,864e5,1728e5,6048e5,2592e6,7776e6,31536e6],al=[[Rs.second,1],[Rs.second,5],[Rs.second,15],[Rs.second,30],[Rs.minute,1],[Rs.minute,5],[Rs.minute,15],[Rs.minute,30],[Rs.hour,1],[Rs.hour,3],[Rs.hour,6],[Rs.hour,12],[Rs.day,1],[Rs.day,2],[Rs.week,1],[Rs.month,1],[Rs.month,3],[Rs.year,1]],cl=[[lo("%Y"),Zt],[lo("%B"),function(n){return n.getMonth()}],[lo("%b %d"),function(n){return 1!=n.getDate()}],[lo("%a %d"),function(n){return n.getDay()&&1!=n.getDate()}],[lo("%I %p"),function(n){return n.getHours()}],[lo("%I:%M"),function(n){return n.getMinutes()}],[lo(":%S"),function(n){return n.getSeconds()}],[lo(".%L"),function(n){return n.getMilliseconds()}]],sl=Zo(cl);al.year=Rs.year,Rs.scale=function(){return Yo($o.scale.linear(),al,sl)};var ll={range:function(n,t,e){return $o.range(+n,+t,e).map(Io)}},fl=al.map(function(n){return[n[0].utc,n[1]]}),hl=[[Fo("%Y"),Zt],[Fo("%B"),function(n){return n.getUTCMonth()}],[Fo("%b %d"),function(n){return 1!=n.getUTCDate()}],[Fo("%a %d"),function(n){return n.getUTCDay()&&1!=n.getUTCDate()}],[Fo("%I %p"),function(n){return n.getUTCHours()}],[Fo("%I:%M"),function(n){return n.getUTCMinutes()}],[Fo(":%S"),function(n){return n.getUTCSeconds()}],[Fo(".%L"),function(n){return n.getUTCMilliseconds()}]],gl=Zo(hl);return fl.year=Rs.year.utc,Rs.scale.utc=function(){return Yo($o.scale.linear(),fl,gl)},$o.text=dt(function(n){return n.responseText}),$o.json=function(n,t){return mt(n,"application/json",Vo,t)},$o.html=function(n,t){return mt(n,"text/html",Xo,t)},$o.xml=dt(function(n){return n.responseXML}),$o}(); \ No newline at end of file diff --git a/vendor/assets/javascripts/minpubsub.js b/vendor/assets/javascripts/minpubsub.js deleted file mode 100644 index 358008d2f2..0000000000 --- a/vendor/assets/javascripts/minpubsub.js +++ /dev/null @@ -1,95 +0,0 @@ -/*! - * MinPubSub - * Copyright(c) 2011 Daniel Lamb - * MIT Licensed - */ -(function (context) { - var MinPubSub = {}; - - // the topic/subscription hash - var cache = context.c_ || {}; //check for 'c_' cache for unit testing - - MinPubSub.publish = function ( /* String */ topic, /* Array? */ args) { - // summary: - // Publish some data on a named topic. - // topic: String - // The channel to publish on - // args: Array? - // The data to publish. Each array item is converted into an ordered - // arguments on the subscribed functions. - // - // example: - // Publish stuff on '/some/topic'. Anything subscribed will be called - // with a function signature like: function(a,b,c){ ... } - // - // publish('/some/topic', ['a','b','c']); - - var subs = cache[topic], - len = subs ? subs.length : 0; - - //can change loop or reverse array if the order matters - while (len--) { - subs[len].apply(context, args || []); - } - }; - - MinPubSub.subscribe = function ( /* String */ topic, /* Function */ callback) { - // summary: - // Register a callback on a named topic. - // topic: String - // The channel to subscribe to - // callback: Function - // The handler event. Anytime something is publish'ed on a - // subscribed channel, the callback will be called with the - // published array as ordered arguments. - // - // returns: Array - // A handle which can be used to unsubscribe this particular subscription. - // - // example: - // subscribe('/some/topic', function(a, b, c){ /* handle data */ }); - - if (!cache[topic]) { - cache[topic] = []; - } - cache[topic].push(callback); - return [topic, callback]; // Array - }; - - MinPubSub.unsubscribe = function ( /* Array */ handle, /* Function? */ callback) { - // summary: - // Disconnect a subscribed function for a topic. - // handle: Array - // The return value from a subscribe call. - // example: - // var handle = subscribe('/some/topic', function(){}); - // unsubscribe(handle); - - var subs = cache[callback ? handle : handle[0]], - callback = callback || handle[1], - len = subs ? subs.length : 0; - - while (len--) { - if (subs[len] === callback) { - subs.splice(len, 1); - } - } - }; - - // UMD definition to allow for CommonJS, AMD and legacy window - if (typeof module === 'object' && module.exports) { - // CommonJS, just export - module.exports = exports = MinPubSub; - } else if (typeof define === 'function' && define.amd) { - // AMD support - define(function () { - return MinPubSub; - }); - } else if (typeof context === 'object') { - // If no AMD and we are in the browser, attach to window - context.publish = MinPubSub.publish; - context.subscribe = MinPubSub.subscribe; - context.unsubscribe = MinPubSub.unsubscribe; - } - -})(this.window); \ No newline at end of file diff --git a/vendor/assets/javascripts/moment.js b/vendor/assets/javascripts/moment.js deleted file mode 100644 index 9c1f3ade9f..0000000000 --- a/vendor/assets/javascripts/moment.js +++ /dev/null @@ -1,6 +0,0 @@ -//! moment.js -//! version : 2.7.0 -//! authors : Tim Wood, Iskren Chernev, Moment.js contributors -//! license : MIT -//! momentjs.com -(function(a){function b(a,b,c){switch(arguments.length){case 2:return null!=a?a:b;case 3:return null!=a?a:null!=b?b:c;default:throw new Error("Implement me")}}function c(){return{empty:!1,unusedTokens:[],unusedInput:[],overflow:-2,charsLeftOver:0,nullInput:!1,invalidMonth:null,invalidFormat:!1,userInvalidated:!1,iso:!1}}function d(a,b){function c(){mb.suppressDeprecationWarnings===!1&&"undefined"!=typeof console&&console.warn&&console.warn("Deprecation warning: "+a)}var d=!0;return j(function(){return d&&(c(),d=!1),b.apply(this,arguments)},b)}function e(a,b){return function(c){return m(a.call(this,c),b)}}function f(a,b){return function(c){return this.lang().ordinal(a.call(this,c),b)}}function g(){}function h(a){z(a),j(this,a)}function i(a){var b=s(a),c=b.year||0,d=b.quarter||0,e=b.month||0,f=b.week||0,g=b.day||0,h=b.hour||0,i=b.minute||0,j=b.second||0,k=b.millisecond||0;this._milliseconds=+k+1e3*j+6e4*i+36e5*h,this._days=+g+7*f,this._months=+e+3*d+12*c,this._data={},this._bubble()}function j(a,b){for(var c in b)b.hasOwnProperty(c)&&(a[c]=b[c]);return b.hasOwnProperty("toString")&&(a.toString=b.toString),b.hasOwnProperty("valueOf")&&(a.valueOf=b.valueOf),a}function k(a){var b,c={};for(b in a)a.hasOwnProperty(b)&&Ab.hasOwnProperty(b)&&(c[b]=a[b]);return c}function l(a){return 0>a?Math.ceil(a):Math.floor(a)}function m(a,b,c){for(var d=""+Math.abs(a),e=a>=0;d.lengthd;d++)(c&&a[d]!==b[d]||!c&&u(a[d])!==u(b[d]))&&g++;return g+f}function r(a){if(a){var b=a.toLowerCase().replace(/(.)s$/,"$1");a=bc[a]||cc[b]||b}return a}function s(a){var b,c,d={};for(c in a)a.hasOwnProperty(c)&&(b=r(c),b&&(d[b]=a[c]));return d}function t(b){var c,d;if(0===b.indexOf("week"))c=7,d="day";else{if(0!==b.indexOf("month"))return;c=12,d="month"}mb[b]=function(e,f){var g,h,i=mb.fn._lang[b],j=[];if("number"==typeof e&&(f=e,e=a),h=function(a){var b=mb().utc().set(d,a);return i.call(mb.fn._lang,b,e||"")},null!=f)return h(f);for(g=0;c>g;g++)j.push(h(g));return j}}function u(a){var b=+a,c=0;return 0!==b&&isFinite(b)&&(c=b>=0?Math.floor(b):Math.ceil(b)),c}function v(a,b){return new Date(Date.UTC(a,b+1,0)).getUTCDate()}function w(a,b,c){return bb(mb([a,11,31+b-c]),b,c).week}function x(a){return y(a)?366:365}function y(a){return a%4===0&&a%100!==0||a%400===0}function z(a){var b;a._a&&-2===a._pf.overflow&&(b=a._a[tb]<0||a._a[tb]>11?tb:a._a[ub]<1||a._a[ub]>v(a._a[sb],a._a[tb])?ub:a._a[vb]<0||a._a[vb]>23?vb:a._a[wb]<0||a._a[wb]>59?wb:a._a[xb]<0||a._a[xb]>59?xb:a._a[yb]<0||a._a[yb]>999?yb:-1,a._pf._overflowDayOfYear&&(sb>b||b>ub)&&(b=ub),a._pf.overflow=b)}function A(a){return null==a._isValid&&(a._isValid=!isNaN(a._d.getTime())&&a._pf.overflow<0&&!a._pf.empty&&!a._pf.invalidMonth&&!a._pf.nullInput&&!a._pf.invalidFormat&&!a._pf.userInvalidated,a._strict&&(a._isValid=a._isValid&&0===a._pf.charsLeftOver&&0===a._pf.unusedTokens.length)),a._isValid}function B(a){return a?a.toLowerCase().replace("_","-"):a}function C(a,b){return b._isUTC?mb(a).zone(b._offset||0):mb(a).local()}function D(a,b){return b.abbr=a,zb[a]||(zb[a]=new g),zb[a].set(b),zb[a]}function E(a){delete zb[a]}function F(a){var b,c,d,e,f=0,g=function(a){if(!zb[a]&&Bb)try{require("./lang/"+a)}catch(b){}return zb[a]};if(!a)return mb.fn._lang;if(!o(a)){if(c=g(a))return c;a=[a]}for(;f0;){if(c=g(e.slice(0,b).join("-")))return c;if(d&&d.length>=b&&q(e,d,!0)>=b-1)break;b--}f++}return mb.fn._lang}function G(a){return a.match(/\[[\s\S]/)?a.replace(/^\[|\]$/g,""):a.replace(/\\/g,"")}function H(a){var b,c,d=a.match(Fb);for(b=0,c=d.length;c>b;b++)d[b]=hc[d[b]]?hc[d[b]]:G(d[b]);return function(e){var f="";for(b=0;c>b;b++)f+=d[b]instanceof Function?d[b].call(e,a):d[b];return f}}function I(a,b){return a.isValid()?(b=J(b,a.lang()),dc[b]||(dc[b]=H(b)),dc[b](a)):a.lang().invalidDate()}function J(a,b){function c(a){return b.longDateFormat(a)||a}var d=5;for(Gb.lastIndex=0;d>=0&&Gb.test(a);)a=a.replace(Gb,c),Gb.lastIndex=0,d-=1;return a}function K(a,b){var c,d=b._strict;switch(a){case"Q":return Rb;case"DDDD":return Tb;case"YYYY":case"GGGG":case"gggg":return d?Ub:Jb;case"Y":case"G":case"g":return Wb;case"YYYYYY":case"YYYYY":case"GGGGG":case"ggggg":return d?Vb:Kb;case"S":if(d)return Rb;case"SS":if(d)return Sb;case"SSS":if(d)return Tb;case"DDD":return Ib;case"MMM":case"MMMM":case"dd":case"ddd":case"dddd":return Mb;case"a":case"A":return F(b._l)._meridiemParse;case"X":return Pb;case"Z":case"ZZ":return Nb;case"T":return Ob;case"SSSS":return Lb;case"MM":case"DD":case"YY":case"GG":case"gg":case"HH":case"hh":case"mm":case"ss":case"ww":case"WW":return d?Sb:Hb;case"M":case"D":case"d":case"H":case"h":case"m":case"s":case"w":case"W":case"e":case"E":return Hb;case"Do":return Qb;default:return c=new RegExp(T(S(a.replace("\\","")),"i"))}}function L(a){a=a||"";var b=a.match(Nb)||[],c=b[b.length-1]||[],d=(c+"").match(_b)||["-",0,0],e=+(60*d[1])+u(d[2]);return"+"===d[0]?-e:e}function M(a,b,c){var d,e=c._a;switch(a){case"Q":null!=b&&(e[tb]=3*(u(b)-1));break;case"M":case"MM":null!=b&&(e[tb]=u(b)-1);break;case"MMM":case"MMMM":d=F(c._l).monthsParse(b),null!=d?e[tb]=d:c._pf.invalidMonth=b;break;case"D":case"DD":null!=b&&(e[ub]=u(b));break;case"Do":null!=b&&(e[ub]=u(parseInt(b,10)));break;case"DDD":case"DDDD":null!=b&&(c._dayOfYear=u(b));break;case"YY":e[sb]=mb.parseTwoDigitYear(b);break;case"YYYY":case"YYYYY":case"YYYYYY":e[sb]=u(b);break;case"a":case"A":c._isPm=F(c._l).isPM(b);break;case"H":case"HH":case"h":case"hh":e[vb]=u(b);break;case"m":case"mm":e[wb]=u(b);break;case"s":case"ss":e[xb]=u(b);break;case"S":case"SS":case"SSS":case"SSSS":e[yb]=u(1e3*("0."+b));break;case"X":c._d=new Date(1e3*parseFloat(b));break;case"Z":case"ZZ":c._useUTC=!0,c._tzm=L(b);break;case"dd":case"ddd":case"dddd":d=F(c._l).weekdaysParse(b),null!=d?(c._w=c._w||{},c._w.d=d):c._pf.invalidWeekday=b;break;case"w":case"ww":case"W":case"WW":case"d":case"e":case"E":a=a.substr(0,1);case"gggg":case"GGGG":case"GGGGG":a=a.substr(0,2),b&&(c._w=c._w||{},c._w[a]=u(b));break;case"gg":case"GG":c._w=c._w||{},c._w[a]=mb.parseTwoDigitYear(b)}}function N(a){var c,d,e,f,g,h,i,j;c=a._w,null!=c.GG||null!=c.W||null!=c.E?(g=1,h=4,d=b(c.GG,a._a[sb],bb(mb(),1,4).year),e=b(c.W,1),f=b(c.E,1)):(j=F(a._l),g=j._week.dow,h=j._week.doy,d=b(c.gg,a._a[sb],bb(mb(),g,h).year),e=b(c.w,1),null!=c.d?(f=c.d,g>f&&++e):f=null!=c.e?c.e+g:g),i=cb(d,e,f,h,g),a._a[sb]=i.year,a._dayOfYear=i.dayOfYear}function O(a){var c,d,e,f,g=[];if(!a._d){for(e=Q(a),a._w&&null==a._a[ub]&&null==a._a[tb]&&N(a),a._dayOfYear&&(f=b(a._a[sb],e[sb]),a._dayOfYear>x(f)&&(a._pf._overflowDayOfYear=!0),d=Z(f,0,a._dayOfYear),a._a[tb]=d.getUTCMonth(),a._a[ub]=d.getUTCDate()),c=0;3>c&&null==a._a[c];++c)a._a[c]=g[c]=e[c];for(;7>c;c++)a._a[c]=g[c]=null==a._a[c]?2===c?1:0:a._a[c];a._d=(a._useUTC?Z:Y).apply(null,g),null!=a._tzm&&a._d.setUTCMinutes(a._d.getUTCMinutes()+a._tzm)}}function P(a){var b;a._d||(b=s(a._i),a._a=[b.year,b.month,b.day,b.hour,b.minute,b.second,b.millisecond],O(a))}function Q(a){var b=new Date;return a._useUTC?[b.getUTCFullYear(),b.getUTCMonth(),b.getUTCDate()]:[b.getFullYear(),b.getMonth(),b.getDate()]}function R(a){if(a._f===mb.ISO_8601)return void V(a);a._a=[],a._pf.empty=!0;var b,c,d,e,f,g=F(a._l),h=""+a._i,i=h.length,j=0;for(d=J(a._f,g).match(Fb)||[],b=0;b0&&a._pf.unusedInput.push(f),h=h.slice(h.indexOf(c)+c.length),j+=c.length),hc[e]?(c?a._pf.empty=!1:a._pf.unusedTokens.push(e),M(e,c,a)):a._strict&&!c&&a._pf.unusedTokens.push(e);a._pf.charsLeftOver=i-j,h.length>0&&a._pf.unusedInput.push(h),a._isPm&&a._a[vb]<12&&(a._a[vb]+=12),a._isPm===!1&&12===a._a[vb]&&(a._a[vb]=0),O(a),z(a)}function S(a){return a.replace(/\\(\[)|\\(\])|\[([^\]\[]*)\]|\\(.)/g,function(a,b,c,d,e){return b||c||d||e})}function T(a){return a.replace(/[-\/\\^$*+?.()|[\]{}]/g,"\\$&")}function U(a){var b,d,e,f,g;if(0===a._f.length)return a._pf.invalidFormat=!0,void(a._d=new Date(0/0));for(f=0;fg)&&(e=g,d=b));j(a,d||b)}function V(a){var b,c,d=a._i,e=Xb.exec(d);if(e){for(a._pf.iso=!0,b=0,c=Zb.length;c>b;b++)if(Zb[b][1].exec(d)){a._f=Zb[b][0]+(e[6]||" ");break}for(b=0,c=$b.length;c>b;b++)if($b[b][1].exec(d)){a._f+=$b[b][0];break}d.match(Nb)&&(a._f+="Z"),R(a)}else a._isValid=!1}function W(a){V(a),a._isValid===!1&&(delete a._isValid,mb.createFromInputFallback(a))}function X(b){var c=b._i,d=Cb.exec(c);c===a?b._d=new Date:d?b._d=new Date(+d[1]):"string"==typeof c?W(b):o(c)?(b._a=c.slice(0),O(b)):p(c)?b._d=new Date(+c):"object"==typeof c?P(b):"number"==typeof c?b._d=new Date(c):mb.createFromInputFallback(b)}function Y(a,b,c,d,e,f,g){var h=new Date(a,b,c,d,e,f,g);return 1970>a&&h.setFullYear(a),h}function Z(a){var b=new Date(Date.UTC.apply(null,arguments));return 1970>a&&b.setUTCFullYear(a),b}function $(a,b){if("string"==typeof a)if(isNaN(a)){if(a=b.weekdaysParse(a),"number"!=typeof a)return null}else a=parseInt(a,10);return a}function _(a,b,c,d,e){return e.relativeTime(b||1,!!c,a,d)}function ab(a,b,c){var d=rb(Math.abs(a)/1e3),e=rb(d/60),f=rb(e/60),g=rb(f/24),h=rb(g/365),i=d0,i[4]=c,_.apply({},i)}function bb(a,b,c){var d,e=c-b,f=c-a.day();return f>e&&(f-=7),e-7>f&&(f+=7),d=mb(a).add("d",f),{week:Math.ceil(d.dayOfYear()/7),year:d.year()}}function cb(a,b,c,d,e){var f,g,h=Z(a,0,1).getUTCDay();return h=0===h?7:h,c=null!=c?c:e,f=e-h+(h>d?7:0)-(e>h?7:0),g=7*(b-1)+(c-e)+f+1,{year:g>0?a:a-1,dayOfYear:g>0?g:x(a-1)+g}}function db(b){var c=b._i,d=b._f;return null===c||d===a&&""===c?mb.invalid({nullInput:!0}):("string"==typeof c&&(b._i=c=F().preparse(c)),mb.isMoment(c)?(b=k(c),b._d=new Date(+c._d)):d?o(d)?U(b):R(b):X(b),new h(b))}function eb(a,b){var c,d;if(1===b.length&&o(b[0])&&(b=b[0]),!b.length)return mb();for(c=b[0],d=1;d=0?"+":"-";return b+m(Math.abs(a),6)},gg:function(){return m(this.weekYear()%100,2)},gggg:function(){return m(this.weekYear(),4)},ggggg:function(){return m(this.weekYear(),5)},GG:function(){return m(this.isoWeekYear()%100,2)},GGGG:function(){return m(this.isoWeekYear(),4)},GGGGG:function(){return m(this.isoWeekYear(),5)},e:function(){return this.weekday()},E:function(){return this.isoWeekday()},a:function(){return this.lang().meridiem(this.hours(),this.minutes(),!0)},A:function(){return this.lang().meridiem(this.hours(),this.minutes(),!1)},H:function(){return this.hours()},h:function(){return this.hours()%12||12},m:function(){return this.minutes()},s:function(){return this.seconds()},S:function(){return u(this.milliseconds()/100)},SS:function(){return m(u(this.milliseconds()/10),2)},SSS:function(){return m(this.milliseconds(),3)},SSSS:function(){return m(this.milliseconds(),3)},Z:function(){var a=-this.zone(),b="+";return 0>a&&(a=-a,b="-"),b+m(u(a/60),2)+":"+m(u(a)%60,2)},ZZ:function(){var a=-this.zone(),b="+";return 0>a&&(a=-a,b="-"),b+m(u(a/60),2)+m(u(a)%60,2)},z:function(){return this.zoneAbbr()},zz:function(){return this.zoneName()},X:function(){return this.unix()},Q:function(){return this.quarter()}},ic=["months","monthsShort","weekdays","weekdaysShort","weekdaysMin"];fc.length;)ob=fc.pop(),hc[ob+"o"]=f(hc[ob],ob);for(;gc.length;)ob=gc.pop(),hc[ob+ob]=e(hc[ob],2);for(hc.DDDD=e(hc.DDD,3),j(g.prototype,{set:function(a){var b,c;for(c in a)b=a[c],"function"==typeof b?this[c]=b:this["_"+c]=b},_months:"January_February_March_April_May_June_July_August_September_October_November_December".split("_"),months:function(a){return this._months[a.month()]},_monthsShort:"Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec".split("_"),monthsShort:function(a){return this._monthsShort[a.month()]},monthsParse:function(a){var b,c,d;for(this._monthsParse||(this._monthsParse=[]),b=0;12>b;b++)if(this._monthsParse[b]||(c=mb.utc([2e3,b]),d="^"+this.months(c,"")+"|^"+this.monthsShort(c,""),this._monthsParse[b]=new RegExp(d.replace(".",""),"i")),this._monthsParse[b].test(a))return b},_weekdays:"Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),weekdays:function(a){return this._weekdays[a.day()]},_weekdaysShort:"Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"),weekdaysShort:function(a){return this._weekdaysShort[a.day()]},_weekdaysMin:"Su_Mo_Tu_We_Th_Fr_Sa".split("_"),weekdaysMin:function(a){return this._weekdaysMin[a.day()]},weekdaysParse:function(a){var b,c,d;for(this._weekdaysParse||(this._weekdaysParse=[]),b=0;7>b;b++)if(this._weekdaysParse[b]||(c=mb([2e3,1]).day(b),d="^"+this.weekdays(c,"")+"|^"+this.weekdaysShort(c,"")+"|^"+this.weekdaysMin(c,""),this._weekdaysParse[b]=new RegExp(d.replace(".",""),"i")),this._weekdaysParse[b].test(a))return b},_longDateFormat:{LT:"h:mm A",L:"MM/DD/YYYY",LL:"MMMM D YYYY",LLL:"MMMM D YYYY LT",LLLL:"dddd, MMMM D YYYY LT"},longDateFormat:function(a){var b=this._longDateFormat[a];return!b&&this._longDateFormat[a.toUpperCase()]&&(b=this._longDateFormat[a.toUpperCase()].replace(/MMMM|MM|DD|dddd/g,function(a){return a.slice(1)}),this._longDateFormat[a]=b),b},isPM:function(a){return"p"===(a+"").toLowerCase().charAt(0)},_meridiemParse:/[ap]\.?m?\.?/i,meridiem:function(a,b,c){return a>11?c?"pm":"PM":c?"am":"AM"},_calendar:{sameDay:"[Today at] LT",nextDay:"[Tomorrow at] LT",nextWeek:"dddd [at] LT",lastDay:"[Yesterday at] LT",lastWeek:"[Last] dddd [at] LT",sameElse:"L"},calendar:function(a,b){var c=this._calendar[a];return"function"==typeof c?c.apply(b):c},_relativeTime:{future:"in %s",past:"%s ago",s:"a few seconds",m:"a minute",mm:"%d minutes",h:"an hour",hh:"%d hours",d:"a day",dd:"%d days",M:"a month",MM:"%d months",y:"a year",yy:"%d years"},relativeTime:function(a,b,c,d){var e=this._relativeTime[c];return"function"==typeof e?e(a,b,c,d):e.replace(/%d/i,a)},pastFuture:function(a,b){var c=this._relativeTime[a>0?"future":"past"];return"function"==typeof c?c(b):c.replace(/%s/i,b)},ordinal:function(a){return this._ordinal.replace("%d",a)},_ordinal:"%d",preparse:function(a){return a},postformat:function(a){return a},week:function(a){return bb(a,this._week.dow,this._week.doy).week},_week:{dow:0,doy:6},_invalidDate:"Invalid date",invalidDate:function(){return this._invalidDate}}),mb=function(b,d,e,f){var g;return"boolean"==typeof e&&(f=e,e=a),g={},g._isAMomentObject=!0,g._i=b,g._f=d,g._l=e,g._strict=f,g._isUTC=!1,g._pf=c(),db(g)},mb.suppressDeprecationWarnings=!1,mb.createFromInputFallback=d("moment construction falls back to js Date. This is discouraged and will be removed in upcoming major release. Please refer to https://github.com/moment/moment/issues/1407 for more info.",function(a){a._d=new Date(a._i)}),mb.min=function(){var a=[].slice.call(arguments,0);return eb("isBefore",a)},mb.max=function(){var a=[].slice.call(arguments,0);return eb("isAfter",a)},mb.utc=function(b,d,e,f){var g;return"boolean"==typeof e&&(f=e,e=a),g={},g._isAMomentObject=!0,g._useUTC=!0,g._isUTC=!0,g._l=e,g._i=b,g._f=d,g._strict=f,g._pf=c(),db(g).utc()},mb.unix=function(a){return mb(1e3*a)},mb.duration=function(a,b){var c,d,e,f=a,g=null;return mb.isDuration(a)?f={ms:a._milliseconds,d:a._days,M:a._months}:"number"==typeof a?(f={},b?f[b]=a:f.milliseconds=a):(g=Db.exec(a))?(c="-"===g[1]?-1:1,f={y:0,d:u(g[ub])*c,h:u(g[vb])*c,m:u(g[wb])*c,s:u(g[xb])*c,ms:u(g[yb])*c}):(g=Eb.exec(a))&&(c="-"===g[1]?-1:1,e=function(a){var b=a&&parseFloat(a.replace(",","."));return(isNaN(b)?0:b)*c},f={y:e(g[2]),M:e(g[3]),d:e(g[4]),h:e(g[5]),m:e(g[6]),s:e(g[7]),w:e(g[8])}),d=new i(f),mb.isDuration(a)&&a.hasOwnProperty("_lang")&&(d._lang=a._lang),d},mb.version=pb,mb.defaultFormat=Yb,mb.ISO_8601=function(){},mb.momentProperties=Ab,mb.updateOffset=function(){},mb.relativeTimeThreshold=function(b,c){return ec[b]===a?!1:(ec[b]=c,!0)},mb.lang=function(a,b){var c;return a?(b?D(B(a),b):null===b?(E(a),a="en"):zb[a]||F(a),c=mb.duration.fn._lang=mb.fn._lang=F(a),c._abbr):mb.fn._lang._abbr},mb.langData=function(a){return a&&a._lang&&a._lang._abbr&&(a=a._lang._abbr),F(a)},mb.isMoment=function(a){return a instanceof h||null!=a&&a.hasOwnProperty("_isAMomentObject")},mb.isDuration=function(a){return a instanceof i},ob=ic.length-1;ob>=0;--ob)t(ic[ob]);mb.normalizeUnits=function(a){return r(a)},mb.invalid=function(a){var b=mb.utc(0/0);return null!=a?j(b._pf,a):b._pf.userInvalidated=!0,b},mb.parseZone=function(){return mb.apply(null,arguments).parseZone()},mb.parseTwoDigitYear=function(a){return u(a)+(u(a)>68?1900:2e3)},j(mb.fn=h.prototype,{clone:function(){return mb(this)},valueOf:function(){return+this._d+6e4*(this._offset||0)},unix:function(){return Math.floor(+this/1e3)},toString:function(){return this.clone().lang("en").format("ddd MMM DD YYYY HH:mm:ss [GMT]ZZ")},toDate:function(){return this._offset?new Date(+this):this._d},toISOString:function(){var a=mb(this).utc();return 00:!1},parsingFlags:function(){return j({},this._pf)},invalidAt:function(){return this._pf.overflow},utc:function(){return this.zone(0)},local:function(){return this.zone(0),this._isUTC=!1,this},format:function(a){var b=I(this,a||mb.defaultFormat);return this.lang().postformat(b)},add:function(a,b){var c;return c="string"==typeof a&&"string"==typeof b?mb.duration(isNaN(+b)?+a:+b,isNaN(+b)?b:a):"string"==typeof a?mb.duration(+b,a):mb.duration(a,b),n(this,c,1),this},subtract:function(a,b){var c;return c="string"==typeof a&&"string"==typeof b?mb.duration(isNaN(+b)?+a:+b,isNaN(+b)?b:a):"string"==typeof a?mb.duration(+b,a):mb.duration(a,b),n(this,c,-1),this},diff:function(a,b,c){var d,e,f=C(a,this),g=6e4*(this.zone()-f.zone());return b=r(b),"year"===b||"month"===b?(d=432e5*(this.daysInMonth()+f.daysInMonth()),e=12*(this.year()-f.year())+(this.month()-f.month()),e+=(this-mb(this).startOf("month")-(f-mb(f).startOf("month")))/d,e-=6e4*(this.zone()-mb(this).startOf("month").zone()-(f.zone()-mb(f).startOf("month").zone()))/d,"year"===b&&(e/=12)):(d=this-f,e="second"===b?d/1e3:"minute"===b?d/6e4:"hour"===b?d/36e5:"day"===b?(d-g)/864e5:"week"===b?(d-g)/6048e5:d),c?e:l(e)},from:function(a,b){return mb.duration(this.diff(a)).lang(this.lang()._abbr).humanize(!b)},fromNow:function(a){return this.from(mb(),a)},calendar:function(a){var b=a||mb(),c=C(b,this).startOf("day"),d=this.diff(c,"days",!0),e=-6>d?"sameElse":-1>d?"lastWeek":0>d?"lastDay":1>d?"sameDay":2>d?"nextDay":7>d?"nextWeek":"sameElse";return this.format(this.lang().calendar(e,this))},isLeapYear:function(){return y(this.year())},isDST:function(){return this.zone()+mb(a).startOf(b)},isBefore:function(a,b){return b="undefined"!=typeof b?b:"millisecond",+this.clone().startOf(b)<+mb(a).startOf(b)},isSame:function(a,b){return b=b||"ms",+this.clone().startOf(b)===+C(a,this).startOf(b)},min:d("moment().min is deprecated, use moment.min instead. https://github.com/moment/moment/issues/1548",function(a){return a=mb.apply(null,arguments),this>a?this:a}),max:d("moment().max is deprecated, use moment.max instead. https://github.com/moment/moment/issues/1548",function(a){return a=mb.apply(null,arguments),a>this?this:a}),zone:function(a,b){var c=this._offset||0;return null==a?this._isUTC?c:this._d.getTimezoneOffset():("string"==typeof a&&(a=L(a)),Math.abs(a)<16&&(a=60*a),this._offset=a,this._isUTC=!0,c!==a&&(!b||this._changeInProgress?n(this,mb.duration(c-a,"m"),1,!1):this._changeInProgress||(this._changeInProgress=!0,mb.updateOffset(this,!0),this._changeInProgress=null)),this)},zoneAbbr:function(){return this._isUTC?"UTC":""},zoneName:function(){return this._isUTC?"Coordinated Universal Time":""},parseZone:function(){return this._tzm?this.zone(this._tzm):"string"==typeof this._i&&this.zone(this._i),this},hasAlignedHourOffset:function(a){return a=a?mb(a).zone():0,(this.zone()-a)%60===0},daysInMonth:function(){return v(this.year(),this.month())},dayOfYear:function(a){var b=rb((mb(this).startOf("day")-mb(this).startOf("year"))/864e5)+1;return null==a?b:this.add("d",a-b)},quarter:function(a){return null==a?Math.ceil((this.month()+1)/3):this.month(3*(a-1)+this.month()%3)},weekYear:function(a){var b=bb(this,this.lang()._week.dow,this.lang()._week.doy).year;return null==a?b:this.add("y",a-b)},isoWeekYear:function(a){var b=bb(this,1,4).year;return null==a?b:this.add("y",a-b)},week:function(a){var b=this.lang().week(this);return null==a?b:this.add("d",7*(a-b))},isoWeek:function(a){var b=bb(this,1,4).week;return null==a?b:this.add("d",7*(a-b))},weekday:function(a){var b=(this.day()+7-this.lang()._week.dow)%7;return null==a?b:this.add("d",a-b)},isoWeekday:function(a){return null==a?this.day()||7:this.day(this.day()%7?a:a-7)},isoWeeksInYear:function(){return w(this.year(),1,4)},weeksInYear:function(){var a=this._lang._week;return w(this.year(),a.dow,a.doy)},get:function(a){return a=r(a),this[a]()},set:function(a,b){return a=r(a),"function"==typeof this[a]&&this[a](b),this},lang:function(b){return b===a?this._lang:(this._lang=F(b),this)}}),mb.fn.millisecond=mb.fn.milliseconds=ib("Milliseconds",!1),mb.fn.second=mb.fn.seconds=ib("Seconds",!1),mb.fn.minute=mb.fn.minutes=ib("Minutes",!1),mb.fn.hour=mb.fn.hours=ib("Hours",!0),mb.fn.date=ib("Date",!0),mb.fn.dates=d("dates accessor is deprecated. Use date instead.",ib("Date",!0)),mb.fn.year=ib("FullYear",!0),mb.fn.years=d("years accessor is deprecated. Use year instead.",ib("FullYear",!0)),mb.fn.days=mb.fn.day,mb.fn.months=mb.fn.month,mb.fn.weeks=mb.fn.week,mb.fn.isoWeeks=mb.fn.isoWeek,mb.fn.quarters=mb.fn.quarter,mb.fn.toJSON=mb.fn.toISOString,j(mb.duration.fn=i.prototype,{_bubble:function(){var a,b,c,d,e=this._milliseconds,f=this._days,g=this._months,h=this._data;h.milliseconds=e%1e3,a=l(e/1e3),h.seconds=a%60,b=l(a/60),h.minutes=b%60,c=l(b/60),h.hours=c%24,f+=l(c/24),h.days=f%30,g+=l(f/30),h.months=g%12,d=l(g/12),h.years=d},weeks:function(){return l(this.days()/7)},valueOf:function(){return this._milliseconds+864e5*this._days+this._months%12*2592e6+31536e6*u(this._months/12)},humanize:function(a){var b=+this,c=ab(b,!a,this.lang());return a&&(c=this.lang().pastFuture(b,c)),this.lang().postformat(c)},add:function(a,b){var c=mb.duration(a,b);return this._milliseconds+=c._milliseconds,this._days+=c._days,this._months+=c._months,this._bubble(),this},subtract:function(a,b){var c=mb.duration(a,b);return this._milliseconds-=c._milliseconds,this._days-=c._days,this._months-=c._months,this._bubble(),this},get:function(a){return a=r(a),this[a.toLowerCase()+"s"]()},as:function(a){return a=r(a),this["as"+a.charAt(0).toUpperCase()+a.slice(1)+"s"]()},lang:mb.fn.lang,toIsoString:function(){var a=Math.abs(this.years()),b=Math.abs(this.months()),c=Math.abs(this.days()),d=Math.abs(this.hours()),e=Math.abs(this.minutes()),f=Math.abs(this.seconds()+this.milliseconds()/1e3);return this.asSeconds()?(this.asSeconds()<0?"-":"")+"P"+(a?a+"Y":"")+(b?b+"M":"")+(c?c+"D":"")+(d||e||f?"T":"")+(d?d+"H":"")+(e?e+"M":"")+(f?f+"S":""):"P0D"}});for(ob in ac)ac.hasOwnProperty(ob)&&(kb(ob,ac[ob]),jb(ob.toLowerCase()));kb("Weeks",6048e5),mb.duration.fn.asMonths=function(){return(+this-31536e6*this.years())/2592e6+12*this.years()},mb.lang("en",{ordinal:function(a){var b=a%10,c=1===u(a%100/10)?"th":1===b?"st":2===b?"nd":3===b?"rd":"th";return a+c}}),Bb?module.exports=mb:"function"==typeof define&&define.amd?(define("moment",function(a,b,c){return c.config&&c.config()&&c.config().noGlobal===!0&&(qb.moment=nb),mb}),lb(!0)):lb()}).call(this); \ No newline at end of file diff --git a/vendor/assets/javascripts/moment.min.js b/vendor/assets/javascripts/moment.min.js deleted file mode 100644 index 3fe82adf13..0000000000 --- a/vendor/assets/javascripts/moment.min.js +++ /dev/null @@ -1,6 +0,0 @@ -//! moment.js -//! version : 2.6.0 -//! authors : Tim Wood, Iskren Chernev, Moment.js contributors -//! license : MIT -//! momentjs.com -(function(a){function b(){return{empty:!1,unusedTokens:[],unusedInput:[],overflow:-2,charsLeftOver:0,nullInput:!1,invalidMonth:null,invalidFormat:!1,userInvalidated:!1,iso:!1}}function c(a,b){function c(){ib.suppressDeprecationWarnings===!1&&"undefined"!=typeof console&&console.warn&&console.warn("Deprecation warning: "+a)}var d=!0;return i(function(){return d&&(c(),d=!1),b.apply(this,arguments)},b)}function d(a,b){return function(c){return l(a.call(this,c),b)}}function e(a,b){return function(c){return this.lang().ordinal(a.call(this,c),b)}}function f(){}function g(a){y(a),i(this,a)}function h(a){var b=r(a),c=b.year||0,d=b.quarter||0,e=b.month||0,f=b.week||0,g=b.day||0,h=b.hour||0,i=b.minute||0,j=b.second||0,k=b.millisecond||0;this._milliseconds=+k+1e3*j+6e4*i+36e5*h,this._days=+g+7*f,this._months=+e+3*d+12*c,this._data={},this._bubble()}function i(a,b){for(var c in b)b.hasOwnProperty(c)&&(a[c]=b[c]);return b.hasOwnProperty("toString")&&(a.toString=b.toString),b.hasOwnProperty("valueOf")&&(a.valueOf=b.valueOf),a}function j(a){var b,c={};for(b in a)a.hasOwnProperty(b)&&wb.hasOwnProperty(b)&&(c[b]=a[b]);return c}function k(a){return 0>a?Math.ceil(a):Math.floor(a)}function l(a,b,c){for(var d=""+Math.abs(a),e=a>=0;d.lengthd;d++)(c&&a[d]!==b[d]||!c&&t(a[d])!==t(b[d]))&&g++;return g+f}function q(a){if(a){var b=a.toLowerCase().replace(/(.)s$/,"$1");a=Zb[a]||$b[b]||b}return a}function r(a){var b,c,d={};for(c in a)a.hasOwnProperty(c)&&(b=q(c),b&&(d[b]=a[c]));return d}function s(b){var c,d;if(0===b.indexOf("week"))c=7,d="day";else{if(0!==b.indexOf("month"))return;c=12,d="month"}ib[b]=function(e,f){var g,h,i=ib.fn._lang[b],j=[];if("number"==typeof e&&(f=e,e=a),h=function(a){var b=ib().utc().set(d,a);return i.call(ib.fn._lang,b,e||"")},null!=f)return h(f);for(g=0;c>g;g++)j.push(h(g));return j}}function t(a){var b=+a,c=0;return 0!==b&&isFinite(b)&&(c=b>=0?Math.floor(b):Math.ceil(b)),c}function u(a,b){return new Date(Date.UTC(a,b+1,0)).getUTCDate()}function v(a,b,c){return $(ib([a,11,31+b-c]),b,c).week}function w(a){return x(a)?366:365}function x(a){return a%4===0&&a%100!==0||a%400===0}function y(a){var b;a._a&&-2===a._pf.overflow&&(b=a._a[pb]<0||a._a[pb]>11?pb:a._a[qb]<1||a._a[qb]>u(a._a[ob],a._a[pb])?qb:a._a[rb]<0||a._a[rb]>23?rb:a._a[sb]<0||a._a[sb]>59?sb:a._a[tb]<0||a._a[tb]>59?tb:a._a[ub]<0||a._a[ub]>999?ub:-1,a._pf._overflowDayOfYear&&(ob>b||b>qb)&&(b=qb),a._pf.overflow=b)}function z(a){return null==a._isValid&&(a._isValid=!isNaN(a._d.getTime())&&a._pf.overflow<0&&!a._pf.empty&&!a._pf.invalidMonth&&!a._pf.nullInput&&!a._pf.invalidFormat&&!a._pf.userInvalidated,a._strict&&(a._isValid=a._isValid&&0===a._pf.charsLeftOver&&0===a._pf.unusedTokens.length)),a._isValid}function A(a){return a?a.toLowerCase().replace("_","-"):a}function B(a,b){return b._isUTC?ib(a).zone(b._offset||0):ib(a).local()}function C(a,b){return b.abbr=a,vb[a]||(vb[a]=new f),vb[a].set(b),vb[a]}function D(a){delete vb[a]}function E(a){var b,c,d,e,f=0,g=function(a){if(!vb[a]&&xb)try{require("./lang/"+a)}catch(b){}return vb[a]};if(!a)return ib.fn._lang;if(!n(a)){if(c=g(a))return c;a=[a]}for(;f0;){if(c=g(e.slice(0,b).join("-")))return c;if(d&&d.length>=b&&p(e,d,!0)>=b-1)break;b--}f++}return ib.fn._lang}function F(a){return a.match(/\[[\s\S]/)?a.replace(/^\[|\]$/g,""):a.replace(/\\/g,"")}function G(a){var b,c,d=a.match(Bb);for(b=0,c=d.length;c>b;b++)d[b]=cc[d[b]]?cc[d[b]]:F(d[b]);return function(e){var f="";for(b=0;c>b;b++)f+=d[b]instanceof Function?d[b].call(e,a):d[b];return f}}function H(a,b){return a.isValid()?(b=I(b,a.lang()),_b[b]||(_b[b]=G(b)),_b[b](a)):a.lang().invalidDate()}function I(a,b){function c(a){return b.longDateFormat(a)||a}var d=5;for(Cb.lastIndex=0;d>=0&&Cb.test(a);)a=a.replace(Cb,c),Cb.lastIndex=0,d-=1;return a}function J(a,b){var c,d=b._strict;switch(a){case"Q":return Nb;case"DDDD":return Pb;case"YYYY":case"GGGG":case"gggg":return d?Qb:Fb;case"Y":case"G":case"g":return Sb;case"YYYYYY":case"YYYYY":case"GGGGG":case"ggggg":return d?Rb:Gb;case"S":if(d)return Nb;case"SS":if(d)return Ob;case"SSS":if(d)return Pb;case"DDD":return Eb;case"MMM":case"MMMM":case"dd":case"ddd":case"dddd":return Ib;case"a":case"A":return E(b._l)._meridiemParse;case"X":return Lb;case"Z":case"ZZ":return Jb;case"T":return Kb;case"SSSS":return Hb;case"MM":case"DD":case"YY":case"GG":case"gg":case"HH":case"hh":case"mm":case"ss":case"ww":case"WW":return d?Ob:Db;case"M":case"D":case"d":case"H":case"h":case"m":case"s":case"w":case"W":case"e":case"E":return Db;case"Do":return Mb;default:return c=new RegExp(R(Q(a.replace("\\","")),"i"))}}function K(a){a=a||"";var b=a.match(Jb)||[],c=b[b.length-1]||[],d=(c+"").match(Xb)||["-",0,0],e=+(60*d[1])+t(d[2]);return"+"===d[0]?-e:e}function L(a,b,c){var d,e=c._a;switch(a){case"Q":null!=b&&(e[pb]=3*(t(b)-1));break;case"M":case"MM":null!=b&&(e[pb]=t(b)-1);break;case"MMM":case"MMMM":d=E(c._l).monthsParse(b),null!=d?e[pb]=d:c._pf.invalidMonth=b;break;case"D":case"DD":null!=b&&(e[qb]=t(b));break;case"Do":null!=b&&(e[qb]=t(parseInt(b,10)));break;case"DDD":case"DDDD":null!=b&&(c._dayOfYear=t(b));break;case"YY":e[ob]=ib.parseTwoDigitYear(b);break;case"YYYY":case"YYYYY":case"YYYYYY":e[ob]=t(b);break;case"a":case"A":c._isPm=E(c._l).isPM(b);break;case"H":case"HH":case"h":case"hh":e[rb]=t(b);break;case"m":case"mm":e[sb]=t(b);break;case"s":case"ss":e[tb]=t(b);break;case"S":case"SS":case"SSS":case"SSSS":e[ub]=t(1e3*("0."+b));break;case"X":c._d=new Date(1e3*parseFloat(b));break;case"Z":case"ZZ":c._useUTC=!0,c._tzm=K(b);break;case"w":case"ww":case"W":case"WW":case"d":case"dd":case"ddd":case"dddd":case"e":case"E":a=a.substr(0,1);case"gg":case"gggg":case"GG":case"GGGG":case"GGGGG":a=a.substr(0,2),b&&(c._w=c._w||{},c._w[a]=b)}}function M(a){var b,c,d,e,f,g,h,i,j,k,l=[];if(!a._d){for(d=O(a),a._w&&null==a._a[qb]&&null==a._a[pb]&&(f=function(b){var c=parseInt(b,10);return b?b.length<3?c>68?1900+c:2e3+c:c:null==a._a[ob]?ib().weekYear():a._a[ob]},g=a._w,null!=g.GG||null!=g.W||null!=g.E?h=_(f(g.GG),g.W||1,g.E,4,1):(i=E(a._l),j=null!=g.d?X(g.d,i):null!=g.e?parseInt(g.e,10)+i._week.dow:0,k=parseInt(g.w,10)||1,null!=g.d&&jw(e)&&(a._pf._overflowDayOfYear=!0),c=W(e,0,a._dayOfYear),a._a[pb]=c.getUTCMonth(),a._a[qb]=c.getUTCDate()),b=0;3>b&&null==a._a[b];++b)a._a[b]=l[b]=d[b];for(;7>b;b++)a._a[b]=l[b]=null==a._a[b]?2===b?1:0:a._a[b];l[rb]+=t((a._tzm||0)/60),l[sb]+=t((a._tzm||0)%60),a._d=(a._useUTC?W:V).apply(null,l)}}function N(a){var b;a._d||(b=r(a._i),a._a=[b.year,b.month,b.day,b.hour,b.minute,b.second,b.millisecond],M(a))}function O(a){var b=new Date;return a._useUTC?[b.getUTCFullYear(),b.getUTCMonth(),b.getUTCDate()]:[b.getFullYear(),b.getMonth(),b.getDate()]}function P(a){a._a=[],a._pf.empty=!0;var b,c,d,e,f,g=E(a._l),h=""+a._i,i=h.length,j=0;for(d=I(a._f,g).match(Bb)||[],b=0;b0&&a._pf.unusedInput.push(f),h=h.slice(h.indexOf(c)+c.length),j+=c.length),cc[e]?(c?a._pf.empty=!1:a._pf.unusedTokens.push(e),L(e,c,a)):a._strict&&!c&&a._pf.unusedTokens.push(e);a._pf.charsLeftOver=i-j,h.length>0&&a._pf.unusedInput.push(h),a._isPm&&a._a[rb]<12&&(a._a[rb]+=12),a._isPm===!1&&12===a._a[rb]&&(a._a[rb]=0),M(a),y(a)}function Q(a){return a.replace(/\\(\[)|\\(\])|\[([^\]\[]*)\]|\\(.)/g,function(a,b,c,d,e){return b||c||d||e})}function R(a){return a.replace(/[-\/\\^$*+?.()|[\]{}]/g,"\\$&")}function S(a){var c,d,e,f,g;if(0===a._f.length)return a._pf.invalidFormat=!0,void(a._d=new Date(0/0));for(f=0;fg)&&(e=g,d=c));i(a,d||c)}function T(a){var b,c,d=a._i,e=Tb.exec(d);if(e){for(a._pf.iso=!0,b=0,c=Vb.length;c>b;b++)if(Vb[b][1].exec(d)){a._f=Vb[b][0]+(e[6]||" ");break}for(b=0,c=Wb.length;c>b;b++)if(Wb[b][1].exec(d)){a._f+=Wb[b][0];break}d.match(Jb)&&(a._f+="Z"),P(a)}else ib.createFromInputFallback(a)}function U(b){var c=b._i,d=yb.exec(c);c===a?b._d=new Date:d?b._d=new Date(+d[1]):"string"==typeof c?T(b):n(c)?(b._a=c.slice(0),M(b)):o(c)?b._d=new Date(+c):"object"==typeof c?N(b):"number"==typeof c?b._d=new Date(c):ib.createFromInputFallback(b)}function V(a,b,c,d,e,f,g){var h=new Date(a,b,c,d,e,f,g);return 1970>a&&h.setFullYear(a),h}function W(a){var b=new Date(Date.UTC.apply(null,arguments));return 1970>a&&b.setUTCFullYear(a),b}function X(a,b){if("string"==typeof a)if(isNaN(a)){if(a=b.weekdaysParse(a),"number"!=typeof a)return null}else a=parseInt(a,10);return a}function Y(a,b,c,d,e){return e.relativeTime(b||1,!!c,a,d)}function Z(a,b,c){var d=nb(Math.abs(a)/1e3),e=nb(d/60),f=nb(e/60),g=nb(f/24),h=nb(g/365),i=45>d&&["s",d]||1===e&&["m"]||45>e&&["mm",e]||1===f&&["h"]||22>f&&["hh",f]||1===g&&["d"]||25>=g&&["dd",g]||45>=g&&["M"]||345>g&&["MM",nb(g/30)]||1===h&&["y"]||["yy",h];return i[2]=b,i[3]=a>0,i[4]=c,Y.apply({},i)}function $(a,b,c){var d,e=c-b,f=c-a.day();return f>e&&(f-=7),e-7>f&&(f+=7),d=ib(a).add("d",f),{week:Math.ceil(d.dayOfYear()/7),year:d.year()}}function _(a,b,c,d,e){var f,g,h=W(a,0,1).getUTCDay();return c=null!=c?c:e,f=e-h+(h>d?7:0)-(e>h?7:0),g=7*(b-1)+(c-e)+f+1,{year:g>0?a:a-1,dayOfYear:g>0?g:w(a-1)+g}}function ab(b){var c=b._i,d=b._f;return null===c||d===a&&""===c?ib.invalid({nullInput:!0}):("string"==typeof c&&(b._i=c=E().preparse(c)),ib.isMoment(c)?(b=j(c),b._d=new Date(+c._d)):d?n(d)?S(b):P(b):U(b),new g(b))}function bb(a,b){var c;return"string"==typeof b&&(b=a.lang().monthsParse(b),"number"!=typeof b)?a:(c=Math.min(a.date(),u(a.year(),b)),a._d["set"+(a._isUTC?"UTC":"")+"Month"](b,c),a)}function cb(a,b){return a._d["get"+(a._isUTC?"UTC":"")+b]()}function db(a,b,c){return"Month"===b?bb(a,c):a._d["set"+(a._isUTC?"UTC":"")+b](c)}function eb(a,b){return function(c){return null!=c?(db(this,a,c),ib.updateOffset(this,b),this):cb(this,a)}}function fb(a){ib.duration.fn[a]=function(){return this._data[a]}}function gb(a,b){ib.duration.fn["as"+a]=function(){return+this/b}}function hb(a){"undefined"==typeof ender&&(jb=mb.moment,mb.moment=a?c("Accessing Moment through the global scope is deprecated, and will be removed in an upcoming release.",ib):ib)}for(var ib,jb,kb,lb="2.6.0",mb="undefined"!=typeof global?global:this,nb=Math.round,ob=0,pb=1,qb=2,rb=3,sb=4,tb=5,ub=6,vb={},wb={_isAMomentObject:null,_i:null,_f:null,_l:null,_strict:null,_isUTC:null,_offset:null,_pf:null,_lang:null},xb="undefined"!=typeof module&&module.exports,yb=/^\/?Date\((\-?\d+)/i,zb=/(\-)?(?:(\d*)\.)?(\d+)\:(\d+)(?:\:(\d+)\.?(\d{3})?)?/,Ab=/^(-)?P(?:(?:([0-9,.]*)Y)?(?:([0-9,.]*)M)?(?:([0-9,.]*)D)?(?:T(?:([0-9,.]*)H)?(?:([0-9,.]*)M)?(?:([0-9,.]*)S)?)?|([0-9,.]*)W)$/,Bb=/(\[[^\[]*\])|(\\)?(Mo|MM?M?M?|Do|DDDo|DD?D?D?|ddd?d?|do?|w[o|w]?|W[o|W]?|Q|YYYYYY|YYYYY|YYYY|YY|gg(ggg?)?|GG(GGG?)?|e|E|a|A|hh?|HH?|mm?|ss?|S{1,4}|X|zz?|ZZ?|.)/g,Cb=/(\[[^\[]*\])|(\\)?(LT|LL?L?L?|l{1,4})/g,Db=/\d\d?/,Eb=/\d{1,3}/,Fb=/\d{1,4}/,Gb=/[+\-]?\d{1,6}/,Hb=/\d+/,Ib=/[0-9]*['a-z\u00A0-\u05FF\u0700-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+|[\u0600-\u06FF\/]+(\s*?[\u0600-\u06FF]+){1,2}/i,Jb=/Z|[\+\-]\d\d:?\d\d/gi,Kb=/T/i,Lb=/[\+\-]?\d+(\.\d{1,3})?/,Mb=/\d{1,2}/,Nb=/\d/,Ob=/\d\d/,Pb=/\d{3}/,Qb=/\d{4}/,Rb=/[+-]?\d{6}/,Sb=/[+-]?\d+/,Tb=/^\s*(?:[+-]\d{6}|\d{4})-(?:(\d\d-\d\d)|(W\d\d$)|(W\d\d-\d)|(\d\d\d))((T| )(\d\d(:\d\d(:\d\d(\.\d+)?)?)?)?([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?$/,Ub="YYYY-MM-DDTHH:mm:ssZ",Vb=[["YYYYYY-MM-DD",/[+-]\d{6}-\d{2}-\d{2}/],["YYYY-MM-DD",/\d{4}-\d{2}-\d{2}/],["GGGG-[W]WW-E",/\d{4}-W\d{2}-\d/],["GGGG-[W]WW",/\d{4}-W\d{2}/],["YYYY-DDD",/\d{4}-\d{3}/]],Wb=[["HH:mm:ss.SSSS",/(T| )\d\d:\d\d:\d\d\.\d+/],["HH:mm:ss",/(T| )\d\d:\d\d:\d\d/],["HH:mm",/(T| )\d\d:\d\d/],["HH",/(T| )\d\d/]],Xb=/([\+\-]|\d\d)/gi,Yb=("Date|Hours|Minutes|Seconds|Milliseconds".split("|"),{Milliseconds:1,Seconds:1e3,Minutes:6e4,Hours:36e5,Days:864e5,Months:2592e6,Years:31536e6}),Zb={ms:"millisecond",s:"second",m:"minute",h:"hour",d:"day",D:"date",w:"week",W:"isoWeek",M:"month",Q:"quarter",y:"year",DDD:"dayOfYear",e:"weekday",E:"isoWeekday",gg:"weekYear",GG:"isoWeekYear"},$b={dayofyear:"dayOfYear",isoweekday:"isoWeekday",isoweek:"isoWeek",weekyear:"weekYear",isoweekyear:"isoWeekYear"},_b={},ac="DDD w W M D d".split(" "),bc="M D H h m s w W".split(" "),cc={M:function(){return this.month()+1},MMM:function(a){return this.lang().monthsShort(this,a)},MMMM:function(a){return this.lang().months(this,a)},D:function(){return this.date()},DDD:function(){return this.dayOfYear()},d:function(){return this.day()},dd:function(a){return this.lang().weekdaysMin(this,a)},ddd:function(a){return this.lang().weekdaysShort(this,a)},dddd:function(a){return this.lang().weekdays(this,a)},w:function(){return this.week()},W:function(){return this.isoWeek()},YY:function(){return l(this.year()%100,2)},YYYY:function(){return l(this.year(),4)},YYYYY:function(){return l(this.year(),5)},YYYYYY:function(){var a=this.year(),b=a>=0?"+":"-";return b+l(Math.abs(a),6)},gg:function(){return l(this.weekYear()%100,2)},gggg:function(){return l(this.weekYear(),4)},ggggg:function(){return l(this.weekYear(),5)},GG:function(){return l(this.isoWeekYear()%100,2)},GGGG:function(){return l(this.isoWeekYear(),4)},GGGGG:function(){return l(this.isoWeekYear(),5)},e:function(){return this.weekday()},E:function(){return this.isoWeekday()},a:function(){return this.lang().meridiem(this.hours(),this.minutes(),!0)},A:function(){return this.lang().meridiem(this.hours(),this.minutes(),!1)},H:function(){return this.hours()},h:function(){return this.hours()%12||12},m:function(){return this.minutes()},s:function(){return this.seconds()},S:function(){return t(this.milliseconds()/100)},SS:function(){return l(t(this.milliseconds()/10),2)},SSS:function(){return l(this.milliseconds(),3)},SSSS:function(){return l(this.milliseconds(),3)},Z:function(){var a=-this.zone(),b="+";return 0>a&&(a=-a,b="-"),b+l(t(a/60),2)+":"+l(t(a)%60,2)},ZZ:function(){var a=-this.zone(),b="+";return 0>a&&(a=-a,b="-"),b+l(t(a/60),2)+l(t(a)%60,2)},z:function(){return this.zoneAbbr()},zz:function(){return this.zoneName()},X:function(){return this.unix()},Q:function(){return this.quarter()}},dc=["months","monthsShort","weekdays","weekdaysShort","weekdaysMin"];ac.length;)kb=ac.pop(),cc[kb+"o"]=e(cc[kb],kb);for(;bc.length;)kb=bc.pop(),cc[kb+kb]=d(cc[kb],2);for(cc.DDDD=d(cc.DDD,3),i(f.prototype,{set:function(a){var b,c;for(c in a)b=a[c],"function"==typeof b?this[c]=b:this["_"+c]=b},_months:"January_February_March_April_May_June_July_August_September_October_November_December".split("_"),months:function(a){return this._months[a.month()]},_monthsShort:"Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec".split("_"),monthsShort:function(a){return this._monthsShort[a.month()]},monthsParse:function(a){var b,c,d;for(this._monthsParse||(this._monthsParse=[]),b=0;12>b;b++)if(this._monthsParse[b]||(c=ib.utc([2e3,b]),d="^"+this.months(c,"")+"|^"+this.monthsShort(c,""),this._monthsParse[b]=new RegExp(d.replace(".",""),"i")),this._monthsParse[b].test(a))return b},_weekdays:"Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),weekdays:function(a){return this._weekdays[a.day()]},_weekdaysShort:"Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"),weekdaysShort:function(a){return this._weekdaysShort[a.day()]},_weekdaysMin:"Su_Mo_Tu_We_Th_Fr_Sa".split("_"),weekdaysMin:function(a){return this._weekdaysMin[a.day()]},weekdaysParse:function(a){var b,c,d;for(this._weekdaysParse||(this._weekdaysParse=[]),b=0;7>b;b++)if(this._weekdaysParse[b]||(c=ib([2e3,1]).day(b),d="^"+this.weekdays(c,"")+"|^"+this.weekdaysShort(c,"")+"|^"+this.weekdaysMin(c,""),this._weekdaysParse[b]=new RegExp(d.replace(".",""),"i")),this._weekdaysParse[b].test(a))return b},_longDateFormat:{LT:"h:mm A",L:"MM/DD/YYYY",LL:"MMMM D YYYY",LLL:"MMMM D YYYY LT",LLLL:"dddd, MMMM D YYYY LT"},longDateFormat:function(a){var b=this._longDateFormat[a];return!b&&this._longDateFormat[a.toUpperCase()]&&(b=this._longDateFormat[a.toUpperCase()].replace(/MMMM|MM|DD|dddd/g,function(a){return a.slice(1)}),this._longDateFormat[a]=b),b},isPM:function(a){return"p"===(a+"").toLowerCase().charAt(0)},_meridiemParse:/[ap]\.?m?\.?/i,meridiem:function(a,b,c){return a>11?c?"pm":"PM":c?"am":"AM"},_calendar:{sameDay:"[Today at] LT",nextDay:"[Tomorrow at] LT",nextWeek:"dddd [at] LT",lastDay:"[Yesterday at] LT",lastWeek:"[Last] dddd [at] LT",sameElse:"L"},calendar:function(a,b){var c=this._calendar[a];return"function"==typeof c?c.apply(b):c},_relativeTime:{future:"in %s",past:"%s ago",s:"a few seconds",m:"a minute",mm:"%d minutes",h:"an hour",hh:"%d hours",d:"a day",dd:"%d days",M:"a month",MM:"%d months",y:"a year",yy:"%d years"},relativeTime:function(a,b,c,d){var e=this._relativeTime[c];return"function"==typeof e?e(a,b,c,d):e.replace(/%d/i,a)},pastFuture:function(a,b){var c=this._relativeTime[a>0?"future":"past"];return"function"==typeof c?c(b):c.replace(/%s/i,b)},ordinal:function(a){return this._ordinal.replace("%d",a)},_ordinal:"%d",preparse:function(a){return a},postformat:function(a){return a},week:function(a){return $(a,this._week.dow,this._week.doy).week},_week:{dow:0,doy:6},_invalidDate:"Invalid date",invalidDate:function(){return this._invalidDate}}),ib=function(c,d,e,f){var g;return"boolean"==typeof e&&(f=e,e=a),g={},g._isAMomentObject=!0,g._i=c,g._f=d,g._l=e,g._strict=f,g._isUTC=!1,g._pf=b(),ab(g)},ib.suppressDeprecationWarnings=!1,ib.createFromInputFallback=c("moment construction falls back to js Date. This is discouraged and will be removed in upcoming major release. Please refer to https://github.com/moment/moment/issues/1407 for more info.",function(a){a._d=new Date(a._i)}),ib.utc=function(c,d,e,f){var g;return"boolean"==typeof e&&(f=e,e=a),g={},g._isAMomentObject=!0,g._useUTC=!0,g._isUTC=!0,g._l=e,g._i=c,g._f=d,g._strict=f,g._pf=b(),ab(g).utc()},ib.unix=function(a){return ib(1e3*a)},ib.duration=function(a,b){var c,d,e,f=a,g=null;return ib.isDuration(a)?f={ms:a._milliseconds,d:a._days,M:a._months}:"number"==typeof a?(f={},b?f[b]=a:f.milliseconds=a):(g=zb.exec(a))?(c="-"===g[1]?-1:1,f={y:0,d:t(g[qb])*c,h:t(g[rb])*c,m:t(g[sb])*c,s:t(g[tb])*c,ms:t(g[ub])*c}):(g=Ab.exec(a))&&(c="-"===g[1]?-1:1,e=function(a){var b=a&&parseFloat(a.replace(",","."));return(isNaN(b)?0:b)*c},f={y:e(g[2]),M:e(g[3]),d:e(g[4]),h:e(g[5]),m:e(g[6]),s:e(g[7]),w:e(g[8])}),d=new h(f),ib.isDuration(a)&&a.hasOwnProperty("_lang")&&(d._lang=a._lang),d},ib.version=lb,ib.defaultFormat=Ub,ib.momentProperties=wb,ib.updateOffset=function(){},ib.lang=function(a,b){var c;return a?(b?C(A(a),b):null===b?(D(a),a="en"):vb[a]||E(a),c=ib.duration.fn._lang=ib.fn._lang=E(a),c._abbr):ib.fn._lang._abbr},ib.langData=function(a){return a&&a._lang&&a._lang._abbr&&(a=a._lang._abbr),E(a)},ib.isMoment=function(a){return a instanceof g||null!=a&&a.hasOwnProperty("_isAMomentObject")},ib.isDuration=function(a){return a instanceof h},kb=dc.length-1;kb>=0;--kb)s(dc[kb]);ib.normalizeUnits=function(a){return q(a)},ib.invalid=function(a){var b=ib.utc(0/0);return null!=a?i(b._pf,a):b._pf.userInvalidated=!0,b},ib.parseZone=function(){return ib.apply(null,arguments).parseZone()},ib.parseTwoDigitYear=function(a){return t(a)+(t(a)>68?1900:2e3)},i(ib.fn=g.prototype,{clone:function(){return ib(this)},valueOf:function(){return+this._d+6e4*(this._offset||0)},unix:function(){return Math.floor(+this/1e3)},toString:function(){return this.clone().lang("en").format("ddd MMM DD YYYY HH:mm:ss [GMT]ZZ")},toDate:function(){return this._offset?new Date(+this):this._d},toISOString:function(){var a=ib(this).utc();return 00:!1},parsingFlags:function(){return i({},this._pf)},invalidAt:function(){return this._pf.overflow},utc:function(){return this.zone(0)},local:function(){return this.zone(0),this._isUTC=!1,this},format:function(a){var b=H(this,a||ib.defaultFormat);return this.lang().postformat(b)},add:function(a,b){var c;return c="string"==typeof a?ib.duration(+b,a):ib.duration(a,b),m(this,c,1),this},subtract:function(a,b){var c;return c="string"==typeof a?ib.duration(+b,a):ib.duration(a,b),m(this,c,-1),this},diff:function(a,b,c){var d,e,f=B(a,this),g=6e4*(this.zone()-f.zone());return b=q(b),"year"===b||"month"===b?(d=432e5*(this.daysInMonth()+f.daysInMonth()),e=12*(this.year()-f.year())+(this.month()-f.month()),e+=(this-ib(this).startOf("month")-(f-ib(f).startOf("month")))/d,e-=6e4*(this.zone()-ib(this).startOf("month").zone()-(f.zone()-ib(f).startOf("month").zone()))/d,"year"===b&&(e/=12)):(d=this-f,e="second"===b?d/1e3:"minute"===b?d/6e4:"hour"===b?d/36e5:"day"===b?(d-g)/864e5:"week"===b?(d-g)/6048e5:d),c?e:k(e)},from:function(a,b){return ib.duration(this.diff(a)).lang(this.lang()._abbr).humanize(!b)},fromNow:function(a){return this.from(ib(),a)},calendar:function(){var a=B(ib(),this).startOf("day"),b=this.diff(a,"days",!0),c=-6>b?"sameElse":-1>b?"lastWeek":0>b?"lastDay":1>b?"sameDay":2>b?"nextDay":7>b?"nextWeek":"sameElse";return this.format(this.lang().calendar(c,this))},isLeapYear:function(){return x(this.year())},isDST:function(){return this.zone()+ib(a).startOf(b)},isBefore:function(a,b){return b="undefined"!=typeof b?b:"millisecond",+this.clone().startOf(b)<+ib(a).startOf(b)},isSame:function(a,b){return b=b||"ms",+this.clone().startOf(b)===+B(a,this).startOf(b)},min:function(a){return a=ib.apply(null,arguments),this>a?this:a},max:function(a){return a=ib.apply(null,arguments),a>this?this:a},zone:function(a,b){var c=this._offset||0;return null==a?this._isUTC?c:this._d.getTimezoneOffset():("string"==typeof a&&(a=K(a)),Math.abs(a)<16&&(a=60*a),this._offset=a,this._isUTC=!0,c!==a&&(!b||this._changeInProgress?m(this,ib.duration(c-a,"m"),1,!1):this._changeInProgress||(this._changeInProgress=!0,ib.updateOffset(this,!0),this._changeInProgress=null)),this)},zoneAbbr:function(){return this._isUTC?"UTC":""},zoneName:function(){return this._isUTC?"Coordinated Universal Time":""},parseZone:function(){return this._tzm?this.zone(this._tzm):"string"==typeof this._i&&this.zone(this._i),this},hasAlignedHourOffset:function(a){return a=a?ib(a).zone():0,(this.zone()-a)%60===0},daysInMonth:function(){return u(this.year(),this.month())},dayOfYear:function(a){var b=nb((ib(this).startOf("day")-ib(this).startOf("year"))/864e5)+1;return null==a?b:this.add("d",a-b)},quarter:function(a){return null==a?Math.ceil((this.month()+1)/3):this.month(3*(a-1)+this.month()%3)},weekYear:function(a){var b=$(this,this.lang()._week.dow,this.lang()._week.doy).year;return null==a?b:this.add("y",a-b)},isoWeekYear:function(a){var b=$(this,1,4).year;return null==a?b:this.add("y",a-b)},week:function(a){var b=this.lang().week(this);return null==a?b:this.add("d",7*(a-b))},isoWeek:function(a){var b=$(this,1,4).week;return null==a?b:this.add("d",7*(a-b))},weekday:function(a){var b=(this.day()+7-this.lang()._week.dow)%7;return null==a?b:this.add("d",a-b)},isoWeekday:function(a){return null==a?this.day()||7:this.day(this.day()%7?a:a-7)},isoWeeksInYear:function(){return v(this.year(),1,4)},weeksInYear:function(){var a=this._lang._week;return v(this.year(),a.dow,a.doy)},get:function(a){return a=q(a),this[a]()},set:function(a,b){return a=q(a),"function"==typeof this[a]&&this[a](b),this},lang:function(b){return b===a?this._lang:(this._lang=E(b),this)}}),ib.fn.millisecond=ib.fn.milliseconds=eb("Milliseconds",!1),ib.fn.second=ib.fn.seconds=eb("Seconds",!1),ib.fn.minute=ib.fn.minutes=eb("Minutes",!1),ib.fn.hour=ib.fn.hours=eb("Hours",!0),ib.fn.date=eb("Date",!0),ib.fn.dates=c("dates accessor is deprecated. Use date instead.",eb("Date",!0)),ib.fn.year=eb("FullYear",!0),ib.fn.years=c("years accessor is deprecated. Use year instead.",eb("FullYear",!0)),ib.fn.days=ib.fn.day,ib.fn.months=ib.fn.month,ib.fn.weeks=ib.fn.week,ib.fn.isoWeeks=ib.fn.isoWeek,ib.fn.quarters=ib.fn.quarter,ib.fn.toJSON=ib.fn.toISOString,i(ib.duration.fn=h.prototype,{_bubble:function(){var a,b,c,d,e=this._milliseconds,f=this._days,g=this._months,h=this._data;h.milliseconds=e%1e3,a=k(e/1e3),h.seconds=a%60,b=k(a/60),h.minutes=b%60,c=k(b/60),h.hours=c%24,f+=k(c/24),h.days=f%30,g+=k(f/30),h.months=g%12,d=k(g/12),h.years=d},weeks:function(){return k(this.days()/7)},valueOf:function(){return this._milliseconds+864e5*this._days+this._months%12*2592e6+31536e6*t(this._months/12)},humanize:function(a){var b=+this,c=Z(b,!a,this.lang());return a&&(c=this.lang().pastFuture(b,c)),this.lang().postformat(c)},add:function(a,b){var c=ib.duration(a,b);return this._milliseconds+=c._milliseconds,this._days+=c._days,this._months+=c._months,this._bubble(),this},subtract:function(a,b){var c=ib.duration(a,b);return this._milliseconds-=c._milliseconds,this._days-=c._days,this._months-=c._months,this._bubble(),this},get:function(a){return a=q(a),this[a.toLowerCase()+"s"]()},as:function(a){return a=q(a),this["as"+a.charAt(0).toUpperCase()+a.slice(1)+"s"]()},lang:ib.fn.lang,toIsoString:function(){var a=Math.abs(this.years()),b=Math.abs(this.months()),c=Math.abs(this.days()),d=Math.abs(this.hours()),e=Math.abs(this.minutes()),f=Math.abs(this.seconds()+this.milliseconds()/1e3);return this.asSeconds()?(this.asSeconds()<0?"-":"")+"P"+(a?a+"Y":"")+(b?b+"M":"")+(c?c+"D":"")+(d||e||f?"T":"")+(d?d+"H":"")+(e?e+"M":"")+(f?f+"S":""):"P0D"}});for(kb in Yb)Yb.hasOwnProperty(kb)&&(gb(kb,Yb[kb]),fb(kb.toLowerCase()));gb("Weeks",6048e5),ib.duration.fn.asMonths=function(){return(+this-31536e6*this.years())/2592e6+12*this.years()},ib.lang("en",{ordinal:function(a){var b=a%10,c=1===t(a%100/10)?"th":1===b?"st":2===b?"nd":3===b?"rd":"th";return a+c}}),xb?module.exports=ib:"function"==typeof define&&define.amd?(define("moment",function(a,b,c){return c.config&&c.config()&&c.config().noGlobal===!0&&(mb.moment=jb),ib}),hb(!0)):hb()}).call(this); \ No newline at end of file diff --git a/vendor/assets/javascripts/require.js b/vendor/assets/javascripts/require.js deleted file mode 100644 index 0c6f152808..0000000000 --- a/vendor/assets/javascripts/require.js +++ /dev/null @@ -1,2037 +0,0 @@ -/** vim: et:ts=4:sw=4:sts=4 - * @license RequireJS 2.0.2 Copyright (c) 2010-2012, The Dojo Foundation All Rights Reserved. - * Available via the MIT or new BSD license. - * see: http://github.com/jrburke/requirejs for details - */ -/*jslint regexp: true, nomen: true */ -/*global window, navigator, document, importScripts, jQuery, setTimeout, opera */ - -var requirejs, require, define; -(function (global) { - 'use strict'; - - var version = '2.0.2', - commentRegExp = /(\/\*([\s\S]*?)\*\/|([^:]|^)\/\/(.*)$)/mg, - cjsRequireRegExp = /require\s*\(\s*["']([^'"\s]+)["']\s*\)/g, - jsSuffixRegExp = /\.js$/, - currDirRegExp = /^\.\//, - ostring = Object.prototype.toString, - ap = Array.prototype, - aps = ap.slice, - apsp = ap.splice, - isBrowser = !!(typeof window !== 'undefined' && navigator && document), - isWebWorker = !isBrowser && typeof importScripts !== 'undefined', - //PS3 indicates loaded and complete, but need to wait for complete - //specifically. Sequence is 'loading', 'loaded', execution, - // then 'complete'. The UA check is unfortunate, but not sure how - //to feature test w/o causing perf issues. - readyRegExp = isBrowser && navigator.platform === 'PLAYSTATION 3' ? - /^complete$/ : /^(complete|loaded)$/, - defContextName = '_', - //Oh the tragedy, detecting opera. See the usage of isOpera for reason. - isOpera = typeof opera !== 'undefined' && opera.toString() === '[object Opera]', - contexts = {}, - cfg = {}, - globalDefQueue = [], - useInteractive = false, - req, s, head, baseElement, dataMain, src, - interactiveScript, currentlyAddingScript, mainScript, subPath; - - function isFunction(it) { - return ostring.call(it) === '[object Function]'; - } - - function isArray(it) { - return ostring.call(it) === '[object Array]'; - } - - /** - * Helper function for iterating over an array. If the func returns - * a true value, it will break out of the loop. - */ - function each(ary, func) { - if (ary) { - var i; - for (i = 0; i < ary.length; i += 1) { - if (ary[i] && func(ary[i], i, ary)) { - break; - } - } - } - } - - /** - * Helper function for iterating over an array backwards. If the func - * returns a true value, it will break out of the loop. - */ - function eachReverse(ary, func) { - if (ary) { - var i; - for (i = ary.length - 1; i > -1; i -= 1) { - if (ary[i] && func(ary[i], i, ary)) { - break; - } - } - } - } - - function hasProp(obj, prop) { - return obj.hasOwnProperty(prop); - } - - /** - * Cycles over properties in an object and calls a function for each - * property value. If the function returns a truthy value, then the - * iteration is stopped. - */ - function eachProp(obj, func) { - var prop; - for (prop in obj) { - if (obj.hasOwnProperty(prop)) { - if (func(obj[prop], prop)) { - break; - } - } - } - } - - /** - * Simple function to mix in properties from source into target, - * but only if target does not already have a property of the same name. - * This is not robust in IE for transferring methods that match - * Object.prototype names, but the uses of mixin here seem unlikely to - * trigger a problem related to that. - */ - function mixin(target, source, force, deepStringMixin) { - if (source) { - eachProp(source, function (value, prop) { - if (force || !hasProp(target, prop)) { - if (deepStringMixin && typeof value !== 'string') { - if (!target[prop]) { - target[prop] = {}; - } - mixin(target[prop], value, force, deepStringMixin); - } else { - target[prop] = value; - } - } - }); - } - return target; - } - - //Similar to Function.prototype.bind, but the 'this' object is specified - //first, since it is easier to read/figure out what 'this' will be. - function bind(obj, fn) { - return function () { - return fn.apply(obj, arguments); - }; - } - - function scripts() { - return document.getElementsByTagName('script'); - } - - //Allow getting a global that expressed in - //dot notation, like 'a.b.c'. - function getGlobal(value) { - if (!value) { - return value; - } - var g = global; - each(value.split('.'), function (part) { - g = g[part]; - }); - return g; - } - - function makeContextModuleFunc(func, relMap, enableBuildCallback) { - return function () { - //A version of a require function that passes a moduleName - //value for items that may need to - //look up paths relative to the moduleName - var args = aps.call(arguments, 0), lastArg; - if (enableBuildCallback && - isFunction((lastArg = args[args.length - 1]))) { - lastArg.__requireJsBuild = true; - } - args.push(relMap); - return func.apply(null, args); - }; - } - - function addRequireMethods(req, context, relMap) { - each([ - ['toUrl'], - ['undef'], - ['defined', 'requireDefined'], - ['specified', 'requireSpecified'] - ], function (item) { - var prop = item[1] || item[0]; - req[item[0]] = context ? makeContextModuleFunc(context[prop], relMap) : - //If no context, then use default context. Reference from - //contexts instead of early binding to default context, so - //that during builds, the latest instance of the default - //context with its config gets used. - function () { - var ctx = contexts[defContextName]; - return ctx[prop].apply(ctx, arguments); - }; - }); - } - - /** - * Constructs an error with a pointer to an URL with more information. - * @param {String} id the error ID that maps to an ID on a web page. - * @param {String} message human readable error. - * @param {Error} [err] the original error, if there is one. - * - * @returns {Error} - */ - function makeError(id, msg, err, requireModules) { - var e = new Error(msg + '\nhttp://requirejs.org/docs/errors.html#' + id); - e.requireType = id; - e.requireModules = requireModules; - if (err) { - e.originalError = err; - } - return e; - } - - if (typeof define !== 'undefined') { - //If a define is already in play via another AMD loader, - //do not overwrite. - return; - } - - if (typeof requirejs !== 'undefined') { - if (isFunction(requirejs)) { - //Do not overwrite and existing requirejs instance. - return; - } - cfg = requirejs; - requirejs = undefined; - } - - //Allow for a require config object - if (typeof require !== 'undefined' && !isFunction(require)) { - //assume it is a config object. - cfg = require; - require = undefined; - } - - function newContext(contextName) { - var config = { - waitSeconds: 7, - baseUrl: './', - paths: {}, - pkgs: {}, - shim: {} - }, - registry = {}, - undefEvents = {}, - defQueue = [], - defined = {}, - urlFetched = {}, - requireCounter = 1, - unnormalizedCounter = 1, - //Used to track the order in which modules - //should be executed, by the order they - //load. Important for consistent cycle resolution - //behavior. - waitAry = [], - inCheckLoaded, Module, context, handlers, - checkLoadedTimeoutId; - - /** - * Trims the . and .. from an array of path segments. - * It will keep a leading path segment if a .. will become - * the first path segment, to help with module name lookups, - * which act like paths, but can be remapped. But the end result, - * all paths that use this function should look normalized. - * NOTE: this method MODIFIES the input array. - * @param {Array} ary the array of path segments. - */ - function trimDots(ary) { - var i, part; - for (i = 0; ary[i]; i+= 1) { - part = ary[i]; - if (part === '.') { - ary.splice(i, 1); - i -= 1; - } else if (part === '..') { - if (i === 1 && (ary[2] === '..' || ary[0] === '..')) { - //End of the line. Keep at least one non-dot - //path segment at the front so it can be mapped - //correctly to disk. Otherwise, there is likely - //no path mapping for a path starting with '..'. - //This can still fail, but catches the most reasonable - //uses of .. - break; - } else if (i > 0) { - ary.splice(i - 1, 2); - i -= 2; - } - } - } - } - - /** - * Given a relative module name, like ./something, normalize it to - * a real name that can be mapped to a path. - * @param {String} name the relative name - * @param {String} baseName a real name that the name arg is relative - * to. - * @param {Boolean} applyMap apply the map config to the value. Should - * only be done if this normalization is for a dependency ID. - * @returns {String} normalized name - */ - function normalize(name, baseName, applyMap) { - var baseParts = baseName && baseName.split('/'), - map = config.map, - starMap = map && map['*'], - pkgName, pkgConfig, mapValue, nameParts, i, j, nameSegment, - foundMap; - - //Adjust any relative paths. - if (name && name.charAt(0) === '.') { - //If have a base name, try to normalize against it, - //otherwise, assume it is a top-level require that will - //be relative to baseUrl in the end. - if (baseName) { - if (config.pkgs[baseName]) { - //If the baseName is a package name, then just treat it as one - //name to concat the name with. - baseParts = [baseName]; - } else { - //Convert baseName to array, and lop off the last part, - //so that . matches that 'directory' and not name of the baseName's - //module. For instance, baseName of 'one/two/three', maps to - //'one/two/three.js', but we want the directory, 'one/two' for - //this normalization. - baseParts = baseParts.slice(0, baseParts.length - 1); - } - - name = baseParts.concat(name.split('/')); - trimDots(name); - - //Some use of packages may use a . path to reference the - //'main' module name, so normalize for that. - pkgConfig = config.pkgs[(pkgName = name[0])]; - name = name.join('/'); - if (pkgConfig && name === pkgName + '/' + pkgConfig.main) { - name = pkgName; - } - } else if (name.indexOf('./') === 0) { - // No baseName, so this is ID is resolved relative - // to baseUrl, pull off the leading dot. - name = name.substring(2); - } - } - - //Apply map config if available. - if (applyMap && (baseParts || starMap) && map) { - nameParts = name.split('/'); - - for (i = nameParts.length; i > 0; i -= 1) { - nameSegment = nameParts.slice(0, i).join('/'); - - if (baseParts) { - //Find the longest baseName segment match in the config. - //So, do joins on the biggest to smallest lengths of baseParts. - for (j = baseParts.length; j > 0; j -= 1) { - mapValue = map[baseParts.slice(0, j).join('/')]; - - //baseName segment has config, find if it has one for - //this name. - if (mapValue) { - mapValue = mapValue[nameSegment]; - if (mapValue) { - //Match, update name to the new value. - foundMap = mapValue; - break; - } - } - } - } - - if (!foundMap && starMap && starMap[nameSegment]) { - foundMap = starMap[nameSegment]; - } - - if (foundMap) { - nameParts.splice(0, i, foundMap); - name = nameParts.join('/'); - break; - } - } - } - - return name; - } - - function removeScript(name) { - if (isBrowser) { - each(scripts(), function (scriptNode) { - if (scriptNode.getAttribute('data-requiremodule') === name && - scriptNode.getAttribute('data-requirecontext') === context.contextName) { - scriptNode.parentNode.removeChild(scriptNode); - return true; - } - }); - } - } - - function hasPathFallback(id) { - var pathConfig = config.paths[id]; - if (pathConfig && isArray(pathConfig) && pathConfig.length > 1) { - removeScript(id); - //Pop off the first array value, since it failed, and - //retry - pathConfig.shift(); - context.undef(id); - context.require([id]); - return true; - } - } - - /** - * Creates a module mapping that includes plugin prefix, module - * name, and path. If parentModuleMap is provided it will - * also normalize the name via require.normalize() - * - * @param {String} name the module name - * @param {String} [parentModuleMap] parent module map - * for the module name, used to resolve relative names. - * @param {Boolean} isNormalized: is the ID already normalized. - * This is true if this call is done for a define() module ID. - * @param {Boolean} applyMap: apply the map config to the ID. - * Should only be true if this map is for a dependency. - * - * @returns {Object} - */ - function makeModuleMap(name, parentModuleMap, isNormalized, applyMap) { - var index = name ? name.indexOf('!') : -1, - prefix = null, - parentName = parentModuleMap ? parentModuleMap.name : null, - originalName = name, - isDefine = true, - normalizedName = '', - url, pluginModule, suffix; - - //If no name, then it means it is a require call, generate an - //internal name. - if (!name) { - isDefine = false; - name = '_@r' + (requireCounter += 1); - } - - if (index !== -1) { - prefix = name.substring(0, index); - name = name.substring(index + 1, name.length); - } - - if (prefix) { - prefix = normalize(prefix, parentName, applyMap); - pluginModule = defined[prefix]; - } - - //Account for relative paths if there is a base name. - if (name) { - if (prefix) { - if (pluginModule && pluginModule.normalize) { - //Plugin is loaded, use its normalize method. - normalizedName = pluginModule.normalize(name, function (name) { - return normalize(name, parentName, applyMap); - }); - } else { - normalizedName = normalize(name, parentName, applyMap); - } - } else { - //A regular module. - normalizedName = normalize(name, parentName, applyMap); - - //Calculate url for the module, if it has a name. - //Use name here since nameToUrl also calls normalize, - //and for relative names that are outside the baseUrl - //this causes havoc. Was thinking of just removing - //parentModuleMap to avoid extra normalization, but - //normalize() still does a dot removal because of - //issue #142, so just pass in name here and redo - //the normalization. Paths outside baseUrl are just - //messy to support. - url = context.nameToUrl(name, null, parentModuleMap); - } - } - - //If the id is a plugin id that cannot be determined if it needs - //normalization, stamp it with a unique ID so two matching relative - //ids that may conflict can be separate. - suffix = prefix && !pluginModule && !isNormalized ? - '_unnormalized' + (unnormalizedCounter += 1) : - ''; - - return { - prefix: prefix, - name: normalizedName, - parentMap: parentModuleMap, - unnormalized: !!suffix, - url: url, - originalName: originalName, - isDefine: isDefine, - id: (prefix ? - prefix + '!' + normalizedName : - normalizedName) + suffix - }; - } - - function getModule(depMap) { - var id = depMap.id, - mod = registry[id]; - - if (!mod) { - mod = registry[id] = new context.Module(depMap); - } - - return mod; - } - - function on(depMap, name, fn) { - var id = depMap.id, - mod = registry[id]; - - if (hasProp(defined, id) && - (!mod || mod.defineEmitComplete)) { - if (name === 'defined') { - fn(defined[id]); - } - } else { - getModule(depMap).on(name, fn); - } - } - - function onError(err, errback) { - var ids = err.requireModules, - notified = false; - - if (errback) { - errback(err); - } else { - each(ids, function (id) { - var mod = registry[id]; - if (mod) { - //Set error on module, so it skips timeout checks. - mod.error = err; - if (mod.events.error) { - notified = true; - mod.emit('error', err); - } - } - }); - - if (!notified) { - req.onError(err); - } - } - } - - /** - * Internal method to transfer globalQueue items to this context's - * defQueue. - */ - function takeGlobalQueue() { - //Push all the globalDefQueue items into the context's defQueue - if (globalDefQueue.length) { - //Array splice in the values since the context code has a - //local var ref to defQueue, so cannot just reassign the one - //on context. - apsp.apply(defQueue, - [defQueue.length - 1, 0].concat(globalDefQueue)); - globalDefQueue = []; - } - } - - /** - * Helper function that creates a require function object to give to - * modules that ask for it as a dependency. It needs to be specific - * per module because of the implication of path mappings that may - * need to be relative to the module name. - */ - function makeRequire(mod, enableBuildCallback, altRequire) { - var relMap = mod && mod.map, - modRequire = makeContextModuleFunc(altRequire || context.require, - relMap, - enableBuildCallback); - - addRequireMethods(modRequire, context, relMap); - modRequire.isBrowser = isBrowser; - - return modRequire; - } - - handlers = { - 'require': function (mod) { - return makeRequire(mod); - }, - 'exports': function (mod) { - mod.usingExports = true; - if (mod.map.isDefine) { - return (mod.exports = defined[mod.map.id] = {}); - } - }, - 'module': function (mod) { - return (mod.module = { - id: mod.map.id, - uri: mod.map.url, - config: function () { - return (config.config && config.config[mod.map.id]) || {}; - }, - exports: defined[mod.map.id] - }); - } - }; - - function removeWaiting(id) { - //Clean up machinery used for waiting modules. - delete registry[id]; - - each(waitAry, function (mod, i) { - if (mod.map.id === id) { - waitAry.splice(i, 1); - if (!mod.defined) { - context.waitCount -= 1; - } - return true; - } - }); - } - - function findCycle(mod, traced) { - var id = mod.map.id, - depArray = mod.depMaps, - foundModule; - - //Do not bother with unitialized modules or not yet enabled - //modules. - if (!mod.inited) { - return; - } - - //Found the cycle. - if (traced[id]) { - return mod; - } - - traced[id] = true; - - //Trace through the dependencies. - each(depArray, function (depMap) { - var depId = depMap.id, - depMod = registry[depId]; - - if (!depMod) { - return; - } - - if (!depMod.inited || !depMod.enabled) { - //Dependency is not inited, so this cannot - //be used to determine a cycle. - foundModule = null; - delete traced[id]; - return true; - } - - //mixin traced to a new object for each dependency, so that - //sibling dependencies in this object to not generate a - //false positive match on a cycle. Ideally an Object.create - //type of prototype delegation would be used here, but - //optimizing for file size vs. execution speed since hopefully - //the trees are small for circular dependency scans relative - //to the full app perf. - return (foundModule = findCycle(depMod, mixin({}, traced))); - }); - - return foundModule; - } - - function forceExec(mod, traced, uninited) { - var id = mod.map.id, - depArray = mod.depMaps; - - if (!mod.inited || !mod.map.isDefine) { - return; - } - - if (traced[id]) { - return defined[id]; - } - - traced[id] = mod; - - each(depArray, function(depMap) { - var depId = depMap.id, - depMod = registry[depId], - value; - - if (handlers[depId]) { - return; - } - - if (depMod) { - if (!depMod.inited || !depMod.enabled) { - //Dependency is not inited, - //so this module cannot be - //given a forced value yet. - uninited[id] = true; - return; - } - - //Get the value for the current dependency - value = forceExec(depMod, traced, uninited); - - //Even with forcing it may not be done, - //in particular if the module is waiting - //on a plugin resource. - if (!uninited[depId]) { - mod.defineDepById(depId, value); - } - } - }); - - mod.check(true); - - return defined[id]; - } - - function modCheck(mod) { - mod.check(); - } - - function checkLoaded() { - var waitInterval = config.waitSeconds * 1000, - //It is possible to disable the wait interval by using waitSeconds of 0. - expired = waitInterval && (context.startTime + waitInterval) < new Date().getTime(), - noLoads = [], - stillLoading = false, - needCycleCheck = true, - map, modId, err, usingPathFallback; - - //Do not bother if this call was a result of a cycle break. - if (inCheckLoaded) { - return; - } - - inCheckLoaded = true; - - //Figure out the state of all the modules. - eachProp(registry, function (mod) { - map = mod.map; - modId = map.id; - - //Skip things that are not enabled or in error state. - if (!mod.enabled) { - return; - } - - if (!mod.error) { - //If the module should be executed, and it has not - //been inited and time is up, remember it. - if (!mod.inited && expired) { - if (hasPathFallback(modId)) { - usingPathFallback = true; - stillLoading = true; - } else { - noLoads.push(modId); - removeScript(modId); - } - } else if (!mod.inited && mod.fetched && map.isDefine) { - stillLoading = true; - if (!map.prefix) { - //No reason to keep looking for unfinished - //loading. If the only stillLoading is a - //plugin resource though, keep going, - //because it may be that a plugin resource - //is waiting on a non-plugin cycle. - return (needCycleCheck = false); - } - } - } - }); - - if (expired && noLoads.length) { - //If wait time expired, throw error of unloaded modules. - err = makeError('timeout', 'Load timeout for modules: ' + noLoads, null, noLoads); - err.contextName = context.contextName; - return onError(err); - } - - //Not expired, check for a cycle. - if (needCycleCheck) { - - each(waitAry, function (mod) { - if (mod.defined) { - return; - } - - var cycleMod = findCycle(mod, {}), - traced = {}; - - if (cycleMod) { - forceExec(cycleMod, traced, {}); - - //traced modules may have been - //removed from the registry, but - //their listeners still need to - //be called. - eachProp(traced, modCheck); - } - }); - - //Now that dependencies have - //been satisfied, trigger the - //completion check that then - //notifies listeners. - eachProp(registry, modCheck); - } - - //If still waiting on loads, and the waiting load is something - //other than a plugin resource, or there are still outstanding - //scripts, then just try back later. - if ((!expired || usingPathFallback) && stillLoading) { - //Something is still waiting to load. Wait for it, but only - //if a timeout is not already in effect. - if ((isBrowser || isWebWorker) && !checkLoadedTimeoutId) { - checkLoadedTimeoutId = setTimeout(function () { - checkLoadedTimeoutId = 0; - checkLoaded(); - }, 50); - } - } - - inCheckLoaded = false; - } - - Module = function (map) { - this.events = undefEvents[map.id] || {}; - this.map = map; - this.shim = config.shim[map.id]; - this.depExports = []; - this.depMaps = []; - this.depMatched = []; - this.pluginMaps = {}; - this.depCount = 0; - - /* this.exports this.factory - this.depMaps = [], - this.enabled, this.fetched - */ - }; - - Module.prototype = { - init: function(depMaps, factory, errback, options) { - options = options || {}; - - //Do not do more inits if already done. Can happen if there - //are multiple define calls for the same module. That is not - //a normal, common case, but it is also not unexpected. - if (this.inited) { - return; - } - - this.factory = factory; - - if (errback) { - //Register for errors on this module. - this.on('error', errback); - } else if (this.events.error) { - //If no errback already, but there are error listeners - //on this module, set up an errback to pass to the deps. - errback = bind(this, function (err) { - this.emit('error', err); - }); - } - - //Do a copy of the dependency array, so that - //source inputs are not modified. For example - //"shim" deps are passed in here directly, and - //doing a direct modification of the depMaps array - //would affect that config. - this.depMaps = depMaps && depMaps.slice(0); - this.depMaps.rjsSkipMap = depMaps.rjsSkipMap; - - this.errback = errback; - - //Indicate this module has be initialized - this.inited = true; - - this.ignore = options.ignore; - - //Could have option to init this module in enabled mode, - //or could have been previously marked as enabled. However, - //the dependencies are not known until init is called. So - //if enabled previously, now trigger dependencies as enabled. - if (options.enabled || this.enabled) { - //Enable this module and dependencies. - //Will call this.check() - this.enable(); - } else { - this.check(); - } - }, - - defineDepById: function (id, depExports) { - var i; - - //Find the index for this dependency. - each(this.depMaps, function (map, index) { - if (map.id === id) { - i = index; - return true; - } - }); - - return this.defineDep(i, depExports); - }, - - defineDep: function (i, depExports) { - //Because of cycles, defined callback for a given - //export can be called more than once. - if (!this.depMatched[i]) { - this.depMatched[i] = true; - this.depCount -= 1; - this.depExports[i] = depExports; - } - }, - - fetch: function () { - if (this.fetched) { - return; - } - this.fetched = true; - - context.startTime = (new Date()).getTime(); - - var map = this.map; - - //If the manager is for a plugin managed resource, - //ask the plugin to load it now. - if (this.shim) { - makeRequire(this, true)(this.shim.deps || [], bind(this, function () { - return map.prefix ? this.callPlugin() : this.load(); - })); - } else { - //Regular dependency. - return map.prefix ? this.callPlugin() : this.load(); - } - }, - - load: function() { - var url = this.map.url; - - //Regular dependency. - if (!urlFetched[url]) { - urlFetched[url] = true; - context.load(this.map.id, url); - } - }, - - /** - * Checks is the module is ready to define itself, and if so, - * define it. If the silent argument is true, then it will just - * define, but not notify listeners, and not ask for a context-wide - * check of all loaded modules. That is useful for cycle breaking. - */ - check: function (silent) { - if (!this.enabled || this.enabling) { - return; - } - - var id = this.map.id, - depExports = this.depExports, - exports = this.exports, - factory = this.factory, - err, cjsModule; - - if (!this.inited) { - this.fetch(); - } else if (this.error) { - this.emit('error', this.error); - } else if (!this.defining) { - //The factory could trigger another require call - //that would result in checking this module to - //define itself again. If already in the process - //of doing that, skip this work. - this.defining = true; - - if (this.depCount < 1 && !this.defined) { - if (isFunction(factory)) { - //If there is an error listener, favor passing - //to that instead of throwing an error. - if (this.events.error) { - try { - exports = context.execCb(id, factory, depExports, exports); - } catch (e) { - err = e; - } - } else { - exports = context.execCb(id, factory, depExports, exports); - } - - if (this.map.isDefine) { - //If setting exports via 'module' is in play, - //favor that over return value and exports. After that, - //favor a non-undefined return value over exports use. - cjsModule = this.module; - if (cjsModule && - cjsModule.exports !== undefined && - //Make sure it is not already the exports value - cjsModule.exports !== this.exports) { - exports = cjsModule.exports; - } else if (exports === undefined && this.usingExports) { - //exports already set the defined value. - exports = this.exports; - } - } - - if (err) { - err.requireMap = this.map; - err.requireModules = [this.map.id]; - err.requireType = 'define'; - return onError((this.error = err)); - } - - } else { - //Just a literal value - exports = factory; - } - - this.exports = exports; - - if (this.map.isDefine && !this.ignore) { - defined[id] = exports; - - if (req.onResourceLoad) { - req.onResourceLoad(context, this.map, this.depMaps); - } - } - - //Clean up - delete registry[id]; - - this.defined = true; - context.waitCount -= 1; - if (context.waitCount === 0) { - //Clear the wait array used for cycles. - waitAry = []; - } - } - - //Finished the define stage. Allow calling check again - //to allow define notifications below in the case of a - //cycle. - this.defining = false; - - if (!silent) { - if (this.defined && !this.defineEmitted) { - this.defineEmitted = true; - this.emit('defined', this.exports); - this.defineEmitComplete = true; - } - } - } - }, - - callPlugin: function() { - var map = this.map, - id = map.id, - pluginMap = makeModuleMap(map.prefix, null, false, true); - - on(pluginMap, 'defined', bind(this, function (plugin) { - var name = this.map.name, - parentName = this.map.parentMap ? this.map.parentMap.name : null, - load, normalizedMap, normalizedMod; - - //If current map is not normalized, wait for that - //normalized name to load instead of continuing. - if (this.map.unnormalized) { - //Normalize the ID if the plugin allows it. - if (plugin.normalize) { - name = plugin.normalize(name, function (name) { - return normalize(name, parentName, true); - }) || ''; - } - - normalizedMap = makeModuleMap(map.prefix + '!' + name, - this.map.parentMap, - false, - true); - on(normalizedMap, - 'defined', bind(this, function (value) { - this.init([], function () { return value; }, null, { - enabled: true, - ignore: true - }); - })); - normalizedMod = registry[normalizedMap.id]; - if (normalizedMod) { - if (this.events.error) { - normalizedMod.on('error', bind(this, function (err) { - this.emit('error', err); - })); - } - normalizedMod.enable(); - } - - return; - } - - load = bind(this, function (value) { - this.init([], function () { return value; }, null, { - enabled: true - }); - }); - - load.error = bind(this, function (err) { - this.inited = true; - this.error = err; - err.requireModules = [id]; - - //Remove temp unnormalized modules for this module, - //since they will never be resolved otherwise now. - eachProp(registry, function (mod) { - if (mod.map.id.indexOf(id + '_unnormalized') === 0) { - removeWaiting(mod.map.id); - } - }); - - onError(err); - }); - - //Allow plugins to load other code without having to know the - //context or how to 'complete' the load. - load.fromText = function (moduleName, text) { - /*jslint evil: true */ - var hasInteractive = useInteractive; - - //Turn off interactive script matching for IE for any define - //calls in the text, then turn it back on at the end. - if (hasInteractive) { - useInteractive = false; - } - - //Prime the system by creating a module instance for - //it. - getModule(makeModuleMap(moduleName)); - - req.exec(text); - - if (hasInteractive) { - useInteractive = true; - } - - //Support anonymous modules. - context.completeLoad(moduleName); - }; - - //Use parentName here since the plugin's name is not reliable, - //could be some weird string with no path that actually wants to - //reference the parentName's path. - plugin.load(map.name, makeRequire(map.parentMap, true, function (deps, cb) { - deps.rjsSkipMap = true; - return context.require(deps, cb); - }), load, config); - })); - - context.enable(pluginMap, this); - this.pluginMaps[pluginMap.id] = pluginMap; - }, - - enable: function () { - this.enabled = true; - - if (!this.waitPushed) { - waitAry.push(this); - context.waitCount += 1; - this.waitPushed = true; - } - - //Set flag mentioning that the module is enabling, - //so that immediate calls to the defined callbacks - //for dependencies do not trigger inadvertent load - //with the depCount still being zero. - this.enabling = true; - - //Enable each dependency - each(this.depMaps, bind(this, function (depMap, i) { - var id, mod, handler; - - if (typeof depMap === 'string') { - //Dependency needs to be converted to a depMap - //and wired up to this module. - depMap = makeModuleMap(depMap, - (this.map.isDefine ? this.map : this.map.parentMap), - false, - !this.depMaps.rjsSkipMap); - this.depMaps[i] = depMap; - - handler = handlers[depMap.id]; - - if (handler) { - this.depExports[i] = handler(this); - return; - } - - this.depCount += 1; - - on(depMap, 'defined', bind(this, function (depExports) { - this.defineDep(i, depExports); - this.check(); - })); - - if (this.errback) { - on(depMap, 'error', this.errback); - } - } - - id = depMap.id; - mod = registry[id]; - - //Skip special modules like 'require', 'exports', 'module' - //Also, don't call enable if it is already enabled, - //important in circular dependency cases. - if (!handlers[id] && mod && !mod.enabled) { - context.enable(depMap, this); - } - })); - - //Enable each plugin that is used in - //a dependency - eachProp(this.pluginMaps, bind(this, function (pluginMap) { - var mod = registry[pluginMap.id]; - if (mod && !mod.enabled) { - context.enable(pluginMap, this); - } - })); - - this.enabling = false; - - this.check(); - }, - - on: function(name, cb) { - var cbs = this.events[name]; - if (!cbs) { - cbs = this.events[name] = []; - } - cbs.push(cb); - }, - - emit: function (name, evt) { - each(this.events[name], function (cb) { - cb(evt); - }); - if (name === 'error') { - //Now that the error handler was triggered, remove - //the listeners, since this broken Module instance - //can stay around for a while in the registry/waitAry. - delete this.events[name]; - } - } - }; - - function callGetModule(args) { - getModule(makeModuleMap(args[0], null, true)).init(args[1], args[2]); - } - - function removeListener(node, func, name, ieName) { - //Favor detachEvent because of IE9 - //issue, see attachEvent/addEventListener comment elsewhere - //in this file. - if (node.detachEvent && !isOpera) { - //Probably IE. If not it will throw an error, which will be - //useful to know. - if (ieName) { - node.detachEvent(ieName, func); - } - } else { - node.removeEventListener(name, func, false); - } - } - - /** - * Given an event from a script node, get the requirejs info from it, - * and then removes the event listeners on the node. - * @param {Event} evt - * @returns {Object} - */ - function getScriptData(evt) { - //Using currentTarget instead of target for Firefox 2.0's sake. Not - //all old browsers will be supported, but this one was easy enough - //to support and still makes sense. - var node = evt.currentTarget || evt.srcElement; - - //Remove the listeners once here. - removeListener(node, context.onScriptLoad, 'load', 'onreadystatechange'); - removeListener(node, context.onScriptError, 'error'); - - return { - node: node, - id: node && node.getAttribute('data-requiremodule') - }; - } - - return (context = { - config: config, - contextName: contextName, - registry: registry, - defined: defined, - urlFetched: urlFetched, - waitCount: 0, - defQueue: defQueue, - Module: Module, - makeModuleMap: makeModuleMap, - - /** - * Set a configuration for the context. - * @param {Object} cfg config object to integrate. - */ - configure: function (cfg) { - //Make sure the baseUrl ends in a slash. - if (cfg.baseUrl) { - if (cfg.baseUrl.charAt(cfg.baseUrl.length - 1) !== '/') { - cfg.baseUrl += '/'; - } - } - - //Save off the paths and packages since they require special processing, - //they are additive. - var pkgs = config.pkgs, - shim = config.shim, - paths = config.paths, - map = config.map; - - //Mix in the config values, favoring the new values over - //existing ones in context.config. - mixin(config, cfg, true); - - //Merge paths. - config.paths = mixin(paths, cfg.paths, true); - - //Merge map - if (cfg.map) { - config.map = mixin(map || {}, cfg.map, true, true); - } - - //Merge shim - if (cfg.shim) { - eachProp(cfg.shim, function (value, id) { - //Normalize the structure - if (isArray(value)) { - value = { - deps: value - }; - } - if (value.exports && !value.exports.__buildReady) { - value.exports = context.makeShimExports(value.exports); - } - shim[id] = value; - }); - config.shim = shim; - } - - //Adjust packages if necessary. - if (cfg.packages) { - each(cfg.packages, function (pkgObj) { - var location; - - pkgObj = typeof pkgObj === 'string' ? { name: pkgObj } : pkgObj; - location = pkgObj.location; - - //Create a brand new object on pkgs, since currentPackages can - //be passed in again, and config.pkgs is the internal transformed - //state for all package configs. - pkgs[pkgObj.name] = { - name: pkgObj.name, - location: location || pkgObj.name, - //Remove leading dot in main, so main paths are normalized, - //and remove any trailing .js, since different package - //envs have different conventions: some use a module name, - //some use a file name. - main: (pkgObj.main || 'main') - .replace(currDirRegExp, '') - .replace(jsSuffixRegExp, '') - }; - }); - - //Done with modifications, assing packages back to context config - config.pkgs = pkgs; - } - - //If there are any "waiting to execute" modules in the registry, - //update the maps for them, since their info, like URLs to load, - //may have changed. - eachProp(registry, function (mod, id) { - mod.map = makeModuleMap(id); - }); - - //If a deps array or a config callback is specified, then call - //require with those args. This is useful when require is defined as a - //config object before require.js is loaded. - if (cfg.deps || cfg.callback) { - context.require(cfg.deps || [], cfg.callback); - } - }, - - makeShimExports: function (exports) { - var func; - if (typeof exports === 'string') { - func = function () { - return getGlobal(exports); - }; - //Save the exports for use in nodefine checking. - func.exports = exports; - return func; - } else { - return function () { - return exports.apply(global, arguments); - }; - } - }, - - requireDefined: function (id, relMap) { - return hasProp(defined, makeModuleMap(id, relMap, false, true).id); - }, - - requireSpecified: function (id, relMap) { - id = makeModuleMap(id, relMap, false, true).id; - return hasProp(defined, id) || hasProp(registry, id); - }, - - require: function (deps, callback, errback, relMap) { - var moduleName, id, map, requireMod, args; - if (typeof deps === 'string') { - if (isFunction(callback)) { - //Invalid call - return onError(makeError('requireargs', 'Invalid require call'), errback); - } - - //Synchronous access to one module. If require.get is - //available (as in the Node adapter), prefer that. - //In this case deps is the moduleName and callback is - //the relMap - if (req.get) { - return req.get(context, deps, callback); - } - - //Just return the module wanted. In this scenario, the - //second arg (if passed) is just the relMap. - moduleName = deps; - relMap = callback; - - //Normalize module name, if it contains . or .. - map = makeModuleMap(moduleName, relMap, false, true); - id = map.id; - - if (!hasProp(defined, id)) { - return onError(makeError('notloaded', 'Module name "' + - id + - '" has not been loaded yet for context: ' + - contextName)); - } - return defined[id]; - } - - //Callback require. Normalize args. if callback or errback is - //not a function, it means it is a relMap. Test errback first. - if (errback && !isFunction(errback)) { - relMap = errback; - errback = undefined; - } - if (callback && !isFunction(callback)) { - relMap = callback; - callback = undefined; - } - - //Any defined modules in the global queue, intake them now. - takeGlobalQueue(); - - //Make sure any remaining defQueue items get properly processed. - while (defQueue.length) { - args = defQueue.shift(); - if (args[0] === null) { - return onError(makeError('mismatch', 'Mismatched anonymous define() module: ' + args[args.length - 1])); - } else { - //args are id, deps, factory. Should be normalized by the - //define() function. - callGetModule(args); - } - } - - //Mark all the dependencies as needing to be loaded. - requireMod = getModule(makeModuleMap(null, relMap)); - - requireMod.init(deps, callback, errback, { - enabled: true - }); - - checkLoaded(); - - return context.require; - }, - - undef: function (id) { - var map = makeModuleMap(id, null, true), - mod = registry[id]; - - delete defined[id]; - delete urlFetched[map.url]; - delete undefEvents[id]; - - if (mod) { - //Hold on to listeners in case the - //module will be attempted to be reloaded - //using a different config. - if (mod.events.defined) { - undefEvents[id] = mod.events; - } - - removeWaiting(id); - } - }, - - /** - * Called to enable a module if it is still in the registry - * awaiting enablement. parent module is passed in for context, - * used by the optimizer. - */ - enable: function (depMap, parent) { - var mod = registry[depMap.id]; - if (mod) { - getModule(depMap).enable(); - } - }, - - /** - * Internal method used by environment adapters to complete a load event. - * A load event could be a script load or just a load pass from a synchronous - * load call. - * @param {String} moduleName the name of the module to potentially complete. - */ - completeLoad: function (moduleName) { - var shim = config.shim[moduleName] || {}, - shExports = shim.exports && shim.exports.exports, - found, args, mod; - - takeGlobalQueue(); - - while (defQueue.length) { - args = defQueue.shift(); - if (args[0] === null) { - args[0] = moduleName; - //If already found an anonymous module and bound it - //to this name, then this is some other anon module - //waiting for its completeLoad to fire. - if (found) { - break; - } - found = true; - } else if (args[0] === moduleName) { - //Found matching define call for this script! - found = true; - } - - callGetModule(args); - } - - //Do this after the cycle of callGetModule in case the result - //of those calls/init calls changes the registry. - mod = registry[moduleName]; - - if (!found && - !defined[moduleName] && - mod && !mod.inited) { - if (config.enforceDefine && (!shExports || !getGlobal(shExports))) { - if (hasPathFallback(moduleName)) { - return; - } else { - return onError(makeError('nodefine', - 'No define call for ' + moduleName, - null, - [moduleName])); - } - } else { - //A script that does not call define(), so just simulate - //the call for it. - callGetModule([moduleName, (shim.deps || []), shim.exports]); - } - } - - checkLoaded(); - }, - - /** - * Converts a module name + .extension into an URL path. - * *Requires* the use of a module name. It does not support using - * plain URLs like nameToUrl. - */ - toUrl: function (moduleNamePlusExt, relModuleMap) { - var index = moduleNamePlusExt.lastIndexOf('.'), - ext = null; - - if (index !== -1) { - ext = moduleNamePlusExt.substring(index, moduleNamePlusExt.length); - moduleNamePlusExt = moduleNamePlusExt.substring(0, index); - } - - return context.nameToUrl(moduleNamePlusExt, ext, relModuleMap); - }, - - /** - * Converts a module name to a file path. Supports cases where - * moduleName may actually be just an URL. - */ - nameToUrl: function (moduleName, ext, relModuleMap) { - var paths, pkgs, pkg, pkgPath, syms, i, parentModule, url, - parentPath; - - //Normalize module name if have a base relative module name to work from. - moduleName = normalize(moduleName, relModuleMap && relModuleMap.id, true); - - //If a colon is in the URL, it indicates a protocol is used and it is just - //an URL to a file, or if it starts with a slash, contains a query arg (i.e. ?) - //or ends with .js, then assume the user meant to use an url and not a module id. - //The slash is important for protocol-less URLs as well as full paths. - if (req.jsExtRegExp.test(moduleName)) { - //Just a plain path, not module name lookup, so just return it. - //Add extension if it is included. This is a bit wonky, only non-.js things pass - //an extension, this method probably needs to be reworked. - url = moduleName + (ext || ''); - } else { - //A module that needs to be converted to a path. - paths = config.paths; - pkgs = config.pkgs; - - syms = moduleName.split('/'); - //For each module name segment, see if there is a path - //registered for it. Start with most specific name - //and work up from it. - for (i = syms.length; i > 0; i -= 1) { - parentModule = syms.slice(0, i).join('/'); - pkg = pkgs[parentModule]; - parentPath = paths[parentModule]; - if (parentPath) { - //If an array, it means there are a few choices, - //Choose the one that is desired - if (isArray(parentPath)) { - parentPath = parentPath[0]; - } - syms.splice(0, i, parentPath); - break; - } else if (pkg) { - //If module name is just the package name, then looking - //for the main module. - if (moduleName === pkg.name) { - pkgPath = pkg.location + '/' + pkg.main; - } else { - pkgPath = pkg.location; - } - syms.splice(0, i, pkgPath); - break; - } - } - - //Join the path parts together, then figure out if baseUrl is needed. - url = syms.join('/') + (ext || '.js'); - url = (url.charAt(0) === '/' || url.match(/^[\w\+\.\-]+:/) ? '' : config.baseUrl) + url; - } - - return config.urlArgs ? url + - ((url.indexOf('?') === -1 ? '?' : '&') + - config.urlArgs) : url; - }, - - //Delegates to req.load. Broken out as a separate function to - //allow overriding in the optimizer. - load: function (id, url) { - req.load(context, id, url); - }, - - /** - * Executes a module callack function. Broken out as a separate function - * solely to allow the build system to sequence the files in the built - * layer in the right sequence. - * - * @private - */ - execCb: function (name, callback, args, exports) { - return callback.apply(exports, args); - }, - - /** - * callback for script loads, used to check status of loading. - * - * @param {Event} evt the event from the browser for the script - * that was loaded. - */ - onScriptLoad: function (evt) { - //Using currentTarget instead of target for Firefox 2.0's sake. Not - //all old browsers will be supported, but this one was easy enough - //to support and still makes sense. - if (evt.type === 'load' || - (readyRegExp.test((evt.currentTarget || evt.srcElement).readyState))) { - //Reset interactive script so a script node is not held onto for - //to long. - interactiveScript = null; - - //Pull out the name of the module and the context. - var data = getScriptData(evt); - context.completeLoad(data.id); - } - }, - - /** - * Callback for script errors. - */ - onScriptError: function (evt) { - var data = getScriptData(evt); - if (!hasPathFallback(data.id)) { - return onError(makeError('scripterror', 'Script error', evt, [data.id])); - } - } - }); - } - - /** - * Main entry point. - * - * If the only argument to require is a string, then the module that - * is represented by that string is fetched for the appropriate context. - * - * If the first argument is an array, then it will be treated as an array - * of dependency string names to fetch. An optional function callback can - * be specified to execute when all of those dependencies are available. - * - * Make a local req variable to help Caja compliance (it assumes things - * on a require that are not standardized), and to give a short - * name for minification/local scope use. - */ - req = requirejs = function (deps, callback, errback, optional) { - - //Find the right context, use default - var contextName = defContextName, - context, config; - - // Determine if have config object in the call. - if (!isArray(deps) && typeof deps !== 'string') { - // deps is a config object - config = deps; - if (isArray(callback)) { - // Adjust args if there are dependencies - deps = callback; - callback = errback; - errback = optional; - } else { - deps = []; - } - } - - if (config && config.context) { - contextName = config.context; - } - - context = contexts[contextName]; - if (!context) { - context = contexts[contextName] = req.s.newContext(contextName); - } - - if (config) { - context.configure(config); - } - - return context.require(deps, callback, errback); - }; - - /** - * Support require.config() to make it easier to cooperate with other - * AMD loaders on globally agreed names. - */ - req.config = function (config) { - return req(config); - }; - - /** - * Export require as a global, but only if it does not already exist. - */ - if (!require) { - require = req; - } - - req.version = version; - - //Used to filter out dependencies that are already paths. - req.jsExtRegExp = /^\/|:|\?|\.js$/; - req.isBrowser = isBrowser; - s = req.s = { - contexts: contexts, - newContext: newContext - }; - - //Create default context. - req({}); - - //Exports some context-sensitive methods on global require, using - //default context if no context specified. - addRequireMethods(req); - - if (isBrowser) { - head = s.head = document.getElementsByTagName('head')[0]; - //If BASE tag is in play, using appendChild is a problem for IE6. - //When that browser dies, this can be removed. Details in this jQuery bug: - //http://dev.jquery.com/ticket/2709 - baseElement = document.getElementsByTagName('base')[0]; - if (baseElement) { - head = s.head = baseElement.parentNode; - } - } - - /** - * Any errors that require explicitly generates will be passed to this - * function. Intercept/override it if you want custom error handling. - * @param {Error} err the error object. - */ - req.onError = function (err) { - throw err; - }; - - /** - * Does the request to load a module for the browser case. - * Make this a separate function to allow other environments - * to override it. - * - * @param {Object} context the require context to find state. - * @param {String} moduleName the name of the module. - * @param {Object} url the URL to the module. - */ - req.load = function (context, moduleName, url) { - var config = (context && context.config) || {}, - node; - if (isBrowser) { - //In the browser so use a script tag - node = config.xhtml ? - document.createElementNS('http://www.w3.org/1999/xhtml', 'html:script') : - document.createElement('script'); - node.type = config.scriptType || 'text/javascript'; - node.charset = 'utf-8'; - - node.setAttribute('data-requirecontext', context.contextName); - node.setAttribute('data-requiremodule', moduleName); - - //Set up load listener. Test attachEvent first because IE9 has - //a subtle issue in its addEventListener and script onload firings - //that do not match the behavior of all other browsers with - //addEventListener support, which fire the onload event for a - //script right after the script execution. See: - //https://connect.microsoft.com/IE/feedback/details/648057/script-onload-event-is-not-fired-immediately-after-script-execution - //UNFORTUNATELY Opera implements attachEvent but does not follow the script - //script execution mode. - if (node.attachEvent && - //Check if node.attachEvent is artificially added by custom script or - //natively supported by browser - //read https://github.com/jrburke/requirejs/issues/187 - //if we can NOT find [native code] then it must NOT natively supported. - //in IE8, node.attachEvent does not have toString() - //Note the test for "[native code" with no closing brace, see: - //https://github.com/jrburke/requirejs/issues/273 - !(node.attachEvent.toString && node.attachEvent.toString().indexOf('[native code') < 0) && - !isOpera) { - //Probably IE. IE (at least 6-8) do not fire - //script onload right after executing the script, so - //we cannot tie the anonymous define call to a name. - //However, IE reports the script as being in 'interactive' - //readyState at the time of the define call. - useInteractive = true; - - node.attachEvent('onreadystatechange', context.onScriptLoad); - //It would be great to add an error handler here to catch - //404s in IE9+. However, onreadystatechange will fire before - //the error handler, so that does not help. If addEvenListener - //is used, then IE will fire error before load, but we cannot - //use that pathway given the connect.microsoft.com issue - //mentioned above about not doing the 'script execute, - //then fire the script load event listener before execute - //next script' that other browsers do. - //Best hope: IE10 fixes the issues, - //and then destroys all installs of IE 6-9. - //node.attachEvent('onerror', context.onScriptError); - } else { - node.addEventListener('load', context.onScriptLoad, false); - node.addEventListener('error', context.onScriptError, false); - } - node.src = url; - - //For some cache cases in IE 6-8, the script executes before the end - //of the appendChild execution, so to tie an anonymous define - //call to the module name (which is stored on the node), hold on - //to a reference to this node, but clear after the DOM insertion. - currentlyAddingScript = node; - if (baseElement) { - head.insertBefore(node, baseElement); - } else { - head.appendChild(node); - } - currentlyAddingScript = null; - - return node; - } else if (isWebWorker) { - //In a web worker, use importScripts. This is not a very - //efficient use of importScripts, importScripts will block until - //its script is downloaded and evaluated. However, if web workers - //are in play, the expectation that a build has been done so that - //only one script needs to be loaded anyway. This may need to be - //reevaluated if other use cases become common. - importScripts(url); - - //Account for anonymous modules - context.completeLoad(moduleName); - } - }; - - function getInteractiveScript() { - if (interactiveScript && interactiveScript.readyState === 'interactive') { - return interactiveScript; - } - - eachReverse(scripts(), function (script) { - if (script.readyState === 'interactive') { - return (interactiveScript = script); - } - }); - return interactiveScript; - } - - //Look for a data-main script attribute, which could also adjust the baseUrl. - if (isBrowser) { - //Figure out baseUrl. Get it from the script tag with require.js in it. - eachReverse(scripts(), function (script) { - //Set the 'head' where we can append children by - //using the script's parent. - if (!head) { - head = script.parentNode; - } - - //Look for a data-main attribute to set main script for the page - //to load. If it is there, the path to data main becomes the - //baseUrl, if it is not already set. - dataMain = script.getAttribute('data-main'); - if (dataMain) { - - //Pull off the directory of data-main for use as the - //baseUrl. - src = dataMain.split('/'); - mainScript = src.pop(); - subPath = src.length ? src.join('/') + '/' : './'; - - //Set final baseUrl if there is not already an explicit one. - if (!cfg.baseUrl) { - cfg.baseUrl = subPath; - } - - //Strip off any trailing .js since dataMain is now - //like a module name. - dataMain = mainScript.replace(jsSuffixRegExp, ''); - - //Put the data-main script in the files to load. - cfg.deps = cfg.deps ? cfg.deps.concat(dataMain) : [dataMain]; - - return true; - } - }); - } - - /** - * The function that handles definitions of modules. Differs from - * require() in that a string for the module should be the first argument, - * and the function to execute after dependencies are loaded should - * return a value to define the module corresponding to the first argument's - * name. - */ - define = function (name, deps, callback) { - var node, context; - - //Allow for anonymous functions - if (typeof name !== 'string') { - //Adjust args appropriately - callback = deps; - deps = name; - name = null; - } - - //This module may not have dependencies - if (!isArray(deps)) { - callback = deps; - deps = []; - } - - //If no name, and callback is a function, then figure out if it a - //CommonJS thing with dependencies. - if (!deps.length && isFunction(callback)) { - //Remove comments from the callback string, - //look for require calls, and pull them into the dependencies, - //but only if there are function args. - if (callback.length) { - callback - .toString() - .replace(commentRegExp, '') - .replace(cjsRequireRegExp, function (match, dep) { - deps.push(dep); - }); - - //May be a CommonJS thing even without require calls, but still - //could use exports, and module. Avoid doing exports and module - //work though if it just needs require. - //REQUIRES the function to expect the CommonJS variables in the - //order listed below. - deps = (callback.length === 1 ? ['require'] : ['require', 'exports', 'module']).concat(deps); - } - } - - //If in IE 6-8 and hit an anonymous define() call, do the interactive - //work. - if (useInteractive) { - node = currentlyAddingScript || getInteractiveScript(); - if (node) { - if (!name) { - name = node.getAttribute('data-requiremodule'); - } - context = contexts[node.getAttribute('data-requirecontext')]; - } - } - - //Always save off evaluating the def call until the script onload handler. - //This allows multiple modules to be in a file without prematurely - //tracing dependencies, and allows for anonymous module support, - //where the module name is not known until the script onload event - //occurs. If no context, use the global queue, and get it processed - //in the onscript load callback. - (context ? context.defQueue : globalDefQueue).push([name, deps, callback]); - }; - - define.amd = { - jQuery: true - }; - - - /** - * Executes the text. Normally just uses eval, but can be modified - * to use a better, environment-specific call. Only used for transpiling - * loader plugins, not for plain JS modules. - * @param {String} text the text to execute/evaluate. - */ - req.exec = function (text) { - /*jslint evil: true */ - return eval(text); - }; - - //Set up with config info. - req(cfg); -}(this)); diff --git a/vendor/assets/javascripts/text.js b/vendor/assets/javascripts/text.js deleted file mode 100644 index 17921b6e5e..0000000000 --- a/vendor/assets/javascripts/text.js +++ /dev/null @@ -1,390 +0,0 @@ -/** - * @license RequireJS text 2.0.12 Copyright (c) 2010-2014, The Dojo Foundation All Rights Reserved. - * Available via the MIT or new BSD license. - * see: http://github.com/requirejs/text for details - */ -/*jslint regexp: true */ -/*global require, XMLHttpRequest, ActiveXObject, - define, window, process, Packages, - java, location, Components, FileUtils */ - -define(['module'], function (module) { - 'use strict'; - - var text, fs, Cc, Ci, xpcIsWindows, - progIds = ['Msxml2.XMLHTTP', 'Microsoft.XMLHTTP', 'Msxml2.XMLHTTP.4.0'], - xmlRegExp = /^\s*<\?xml(\s)+version=[\'\"](\d)*.(\d)*[\'\"](\s)*\?>/im, - bodyRegExp = /]*>\s*([\s\S]+)\s*<\/body>/im, - hasLocation = typeof location !== 'undefined' && location.href, - defaultProtocol = hasLocation && location.protocol && location.protocol.replace(/\:/, ''), - defaultHostName = hasLocation && location.hostname, - defaultPort = hasLocation && (location.port || undefined), - buildMap = {}, - masterConfig = (module.config && module.config()) || {}; - - text = { - version: '2.0.12', - - strip: function (content) { - //Strips declarations so that external SVG and XML - //documents can be added to a document without worry. Also, if the string - //is an HTML document, only the part inside the body tag is returned. - if (content) { - content = content.replace(xmlRegExp, ""); - var matches = content.match(bodyRegExp); - if (matches) { - content = matches[1]; - } - } else { - content = ""; - } - return content; - }, - - jsEscape: function (content) { - return content.replace(/(['\\])/g, '\\$1') - .replace(/[\f]/g, "\\f") - .replace(/[\b]/g, "\\b") - .replace(/[\n]/g, "\\n") - .replace(/[\t]/g, "\\t") - .replace(/[\r]/g, "\\r") - .replace(/[\u2028]/g, "\\u2028") - .replace(/[\u2029]/g, "\\u2029"); - }, - - createXhr: masterConfig.createXhr || function () { - //Would love to dump the ActiveX crap in here. Need IE 6 to die first. - var xhr, i, progId; - if (typeof XMLHttpRequest !== "undefined") { - return new XMLHttpRequest(); - } else if (typeof ActiveXObject !== "undefined") { - for (i = 0; i < 3; i += 1) { - progId = progIds[i]; - try { - xhr = new ActiveXObject(progId); - } catch (e) {} - - if (xhr) { - progIds = [progId]; // so faster next time - break; - } - } - } - - return xhr; - }, - - /** - * Parses a resource name into its component parts. Resource names - * look like: module/name.ext!strip, where the !strip part is - * optional. - * @param {String} name the resource name - * @returns {Object} with properties "moduleName", "ext" and "strip" - * where strip is a boolean. - */ - parseName: function (name) { - var modName, ext, temp, - strip = false, - index = name.indexOf("."), - isRelative = name.indexOf('./') === 0 || - name.indexOf('../') === 0; - - if (index !== -1 && (!isRelative || index > 1)) { - modName = name.substring(0, index); - ext = name.substring(index + 1, name.length); - } else { - modName = name; - } - - temp = ext || modName; - index = temp.indexOf("!"); - if (index !== -1) { - //Pull off the strip arg. - strip = temp.substring(index + 1) === "strip"; - temp = temp.substring(0, index); - if (ext) { - ext = temp; - } else { - modName = temp; - } - } - - return { - moduleName: modName, - ext: ext, - strip: strip - }; - }, - - xdRegExp: /^((\w+)\:)?\/\/([^\/\\]+)/, - - /** - * Is an URL on another domain. Only works for browser use, returns - * false in non-browser environments. Only used to know if an - * optimized .js version of a text resource should be loaded - * instead. - * @param {String} url - * @returns Boolean - */ - useXhr: function (url, protocol, hostname, port) { - var uProtocol, uHostName, uPort, - match = text.xdRegExp.exec(url); - if (!match) { - return true; - } - uProtocol = match[2]; - uHostName = match[3]; - - uHostName = uHostName.split(':'); - uPort = uHostName[1]; - uHostName = uHostName[0]; - - return (!uProtocol || uProtocol === protocol) && - (!uHostName || uHostName.toLowerCase() === hostname.toLowerCase()) && - ((!uPort && !uHostName) || uPort === port); - }, - - finishLoad: function (name, strip, content, onLoad) { - content = strip ? text.strip(content) : content; - if (masterConfig.isBuild) { - buildMap[name] = content; - } - onLoad(content); - }, - - load: function (name, req, onLoad, config) { - //Name has format: some.module.filext!strip - //The strip part is optional. - //if strip is present, then that means only get the string contents - //inside a body tag in an HTML string. For XML/SVG content it means - //removing the declarations so the content can be inserted - //into the current doc without problems. - - // Do not bother with the work if a build and text will - // not be inlined. - if (config && config.isBuild && !config.inlineText) { - onLoad(); - return; - } - - masterConfig.isBuild = config && config.isBuild; - - var parsed = text.parseName(name), - nonStripName = parsed.moduleName + - (parsed.ext ? '.' + parsed.ext : ''), - url = req.toUrl(nonStripName), - useXhr = (masterConfig.useXhr) || - text.useXhr; - - // Do not load if it is an empty: url - if (url.indexOf('empty:') === 0) { - onLoad(); - return; - } - - //Load the text. Use XHR if possible and in a browser. - if (!hasLocation || useXhr(url, defaultProtocol, defaultHostName, defaultPort)) { - text.get(url, function (content) { - text.finishLoad(name, parsed.strip, content, onLoad); - }, function (err) { - if (onLoad.error) { - onLoad.error(err); - } - }); - } else { - //Need to fetch the resource across domains. Assume - //the resource has been optimized into a JS module. Fetch - //by the module name + extension, but do not include the - //!strip part to avoid file system issues. - req([nonStripName], function (content) { - text.finishLoad(parsed.moduleName + '.' + parsed.ext, - parsed.strip, content, onLoad); - }); - } - }, - - write: function (pluginName, moduleName, write, config) { - if (buildMap.hasOwnProperty(moduleName)) { - var content = text.jsEscape(buildMap[moduleName]); - write.asModule(pluginName + "!" + moduleName, - "define(function () { return '" + - content + - "';});\n"); - } - }, - - writeFile: function (pluginName, moduleName, req, write, config) { - var parsed = text.parseName(moduleName), - extPart = parsed.ext ? '.' + parsed.ext : '', - nonStripName = parsed.moduleName + extPart, - //Use a '.js' file name so that it indicates it is a - //script that can be loaded across domains. - fileName = req.toUrl(parsed.moduleName + extPart) + '.js'; - - //Leverage own load() method to load plugin value, but only - //write out values that do not have the strip argument, - //to avoid any potential issues with ! in file names. - text.load(nonStripName, req, function (value) { - //Use own write() method to construct full module value. - //But need to create shell that translates writeFile's - //write() to the right interface. - var textWrite = function (contents) { - return write(fileName, contents); - }; - textWrite.asModule = function (moduleName, contents) { - return write.asModule(moduleName, fileName, contents); - }; - - text.write(pluginName, nonStripName, textWrite, config); - }, config); - } - }; - - if (masterConfig.env === 'node' || (!masterConfig.env && - typeof process !== "undefined" && - process.versions && - !!process.versions.node && - !process.versions['node-webkit'])) { - //Using special require.nodeRequire, something added by r.js. - fs = require.nodeRequire('fs'); - - text.get = function (url, callback, errback) { - try { - var file = fs.readFileSync(url, 'utf8'); - //Remove BOM (Byte Mark Order) from utf8 files if it is there. - if (file.indexOf('\uFEFF') === 0) { - file = file.substring(1); - } - callback(file); - } catch (e) { - if (errback) { - errback(e); - } - } - }; - } else if (masterConfig.env === 'xhr' || (!masterConfig.env && - text.createXhr())) { - text.get = function (url, callback, errback, headers) { - var xhr = text.createXhr(), header; - xhr.open('GET', url, true); - - //Allow plugins direct access to xhr headers - if (headers) { - for (header in headers) { - if (headers.hasOwnProperty(header)) { - xhr.setRequestHeader(header.toLowerCase(), headers[header]); - } - } - } - - //Allow overrides specified in config - if (masterConfig.onXhr) { - masterConfig.onXhr(xhr, url); - } - - xhr.onreadystatechange = function (evt) { - var status, err; - //Do not explicitly handle errors, those should be - //visible via console output in the browser. - if (xhr.readyState === 4) { - status = xhr.status || 0; - if (status > 399 && status < 600) { - //An http 4xx or 5xx error. Signal an error. - err = new Error(url + ' HTTP status: ' + status); - err.xhr = xhr; - if (errback) { - errback(err); - } - } else { - callback(xhr.responseText); - } - - if (masterConfig.onXhrComplete) { - masterConfig.onXhrComplete(xhr, url); - } - } - }; - xhr.send(null); - }; - } else if (masterConfig.env === 'rhino' || (!masterConfig.env && - typeof Packages !== 'undefined' && typeof java !== 'undefined')) { - //Why Java, why is this so awkward? - text.get = function (url, callback) { - var stringBuffer, line, - encoding = "utf-8", - file = new java.io.File(url), - lineSeparator = java.lang.System.getProperty("line.separator"), - input = new java.io.BufferedReader(new java.io.InputStreamReader(new java.io.FileInputStream(file), encoding)), - content = ''; - try { - stringBuffer = new java.lang.StringBuffer(); - line = input.readLine(); - - // Byte Order Mark (BOM) - The Unicode Standard, version 3.0, page 324 - // http://www.unicode.org/faq/utf_bom.html - - // Note that when we use utf-8, the BOM should appear as "EF BB BF", but it doesn't due to this bug in the JDK: - // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4508058 - if (line && line.length() && line.charAt(0) === 0xfeff) { - // Eat the BOM, since we've already found the encoding on this file, - // and we plan to concatenating this buffer with others; the BOM should - // only appear at the top of a file. - line = line.substring(1); - } - - if (line !== null) { - stringBuffer.append(line); - } - - while ((line = input.readLine()) !== null) { - stringBuffer.append(lineSeparator); - stringBuffer.append(line); - } - //Make sure we return a JavaScript string and not a Java string. - content = String(stringBuffer.toString()); //String - } finally { - input.close(); - } - callback(content); - }; - } else if (masterConfig.env === 'xpconnect' || (!masterConfig.env && - typeof Components !== 'undefined' && Components.classes && - Components.interfaces)) { - //Avert your gaze! - Cc = Components.classes; - Ci = Components.interfaces; - Components.utils['import']('resource://gre/modules/FileUtils.jsm'); - xpcIsWindows = ('@mozilla.org/windows-registry-key;1' in Cc); - - text.get = function (url, callback) { - var inStream, convertStream, fileObj, - readData = {}; - - if (xpcIsWindows) { - url = url.replace(/\//g, '\\'); - } - - fileObj = new FileUtils.File(url); - - //XPCOM, you so crazy - try { - inStream = Cc['@mozilla.org/network/file-input-stream;1'] - .createInstance(Ci.nsIFileInputStream); - inStream.init(fileObj, 1, 0, false); - - convertStream = Cc['@mozilla.org/intl/converter-input-stream;1'] - .createInstance(Ci.nsIConverterInputStream); - convertStream.init(inStream, "utf-8", inStream.available(), - Ci.nsIConverterInputStream.DEFAULT_REPLACEMENT_CHARACTER); - - convertStream.readString(inStream.available(), readData); - convertStream.close(); - inStream.close(); - callback(readData.value); - } catch (e) { - throw new Error((fileObj && fileObj.path || '') + ': ' + e); - } - }; - } - return text; -}); diff --git a/vendor/assets/javascripts/underscore.js b/vendor/assets/javascripts/underscore.js deleted file mode 100644 index 4a9a24947a..0000000000 --- a/vendor/assets/javascripts/underscore.js +++ /dev/null @@ -1,1399 +0,0 @@ -// Underscore.js 1.6.0 -// http://underscorejs.org -// (c) 2009-2014 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors -// Underscore may be freely distributed under the MIT license. - -(function() { - - // Baseline setup - // -------------- - - // Establish the root object, `window` in the browser, or `exports` on the server. - var root = this; - - // Save the previous value of the `_` variable. - var previousUnderscore = root._; - - // Establish the object that gets returned to break out of a loop iteration. - var breaker = {}; - - // Save bytes in the minified (but not gzipped) version: - var ArrayProto = Array.prototype, ObjProto = Object.prototype, FuncProto = Function.prototype; - - // Create quick reference variables for speed access to core prototypes. - var - push = ArrayProto.push, - slice = ArrayProto.slice, - concat = ArrayProto.concat, - toString = ObjProto.toString, - hasOwnProperty = ObjProto.hasOwnProperty; - - // All **ECMAScript 5** native function implementations that we hope to use - // are declared here. - var - nativeIsArray = Array.isArray, - nativeKeys = Object.keys, - nativeBind = FuncProto.bind; - - // Create a safe reference to the Underscore object for use below. - var _ = function(obj) { - if (obj instanceof _) return obj; - if (!(this instanceof _)) return new _(obj); - this._wrapped = obj; - }; - - // Export the Underscore object for **Node.js**, with - // backwards-compatibility for the old `require()` API. If we're in - // the browser, add `_` as a global object via a string identifier, - // for Closure Compiler "advanced" mode. - if (typeof exports !== 'undefined') { - if (typeof module !== 'undefined' && module.exports) { - exports = module.exports = _; - } - exports._ = _; - } else { - root._ = _; - } - - // Current version. - _.VERSION = '1.6.0'; - - // Internal function: creates a callback bound to its context if supplied - var createCallback = function(func, context, argCount) { - if (context === void 0) return func; - switch (argCount == null ? 3 : argCount) { - case 1: return function(value) { - return func.call(context, value); - }; - case 2: return function(value, other) { - return func.call(context, value, other); - }; - case 3: return function(value, index, collection) { - return func.call(context, value, index, collection); - }; - case 4: return function(accumulator, value, index, collection) { - return func.call(context, accumulator, value, index, collection); - }; - } - return function() { - return func.apply(context, arguments); - }; - }; - - // An internal function to generate lookup iterators. - var lookupIterator = function(value, context, argCount) { - if (value == null) return _.identity; - if (_.isFunction(value)) return createCallback(value, context, argCount); - if (_.isObject(value)) return _.matches(value); - return _.property(value); - }; - - // Collection Functions - // -------------------- - - // The cornerstone, an `each` implementation, aka `forEach`. - // Handles raw objects in addition to array-likes. Treats all - // sparse array-likes as if they were dense. - _.each = _.forEach = function(obj, iterator, context) { - var i, length; - if (obj == null) return obj; - iterator = createCallback(iterator, context); - if (obj.length === +obj.length) { - for (i = 0, length = obj.length; i < length; i++) { - if (iterator(obj[i], i, obj) === breaker) break; - } - } else { - var keys = _.keys(obj); - for (i = 0, length = keys.length; i < length; i++) { - if (iterator(obj[keys[i]], keys[i], obj) === breaker) break; - } - } - return obj; - }; - - // Return the results of applying the iterator to each element. - _.map = _.collect = function(obj, iterator, context) { - var results = []; - if (obj == null) return results; - iterator = lookupIterator(iterator, context); - _.each(obj, function(value, index, list) { - results.push(iterator(value, index, list)); - }); - return results; - }; - - var reduceError = 'Reduce of empty array with no initial value'; - - // **Reduce** builds up a single result from a list of values, aka `inject`, - // or `foldl`. - _.reduce = _.foldl = _.inject = function(obj, iterator, memo, context) { - var initial = arguments.length > 2; - if (obj == null) obj = []; - iterator = createCallback(iterator, context, 4); - _.each(obj, function(value, index, list) { - if (!initial) { - memo = value; - initial = true; - } else { - memo = iterator(memo, value, index, list); - } - }); - if (!initial) throw TypeError(reduceError); - return memo; - }; - - // The right-associative version of reduce, also known as `foldr`. - _.reduceRight = _.foldr = function(obj, iterator, memo, context) { - var initial = arguments.length > 2; - if (obj == null) obj = []; - var length = obj.length; - iterator = createCallback(iterator, context, 4); - if (length !== +length) { - var keys = _.keys(obj); - length = keys.length; - } - _.each(obj, function(value, index, list) { - index = keys ? keys[--length] : --length; - if (!initial) { - memo = obj[index]; - initial = true; - } else { - memo = iterator(memo, obj[index], index, list); - } - }); - if (!initial) throw TypeError(reduceError); - return memo; - }; - - // Return the first value which passes a truth test. Aliased as `detect`. - _.find = _.detect = function(obj, predicate, context) { - var result; - predicate = lookupIterator(predicate, context); - _.some(obj, function(value, index, list) { - if (predicate(value, index, list)) { - result = value; - return true; - } - }); - return result; - }; - - // Return all the elements that pass a truth test. - // Aliased as `select`. - _.filter = _.select = function(obj, predicate, context) { - var results = []; - if (obj == null) return results; - predicate = lookupIterator(predicate, context); - _.each(obj, function(value, index, list) { - if (predicate(value, index, list)) results.push(value); - }); - return results; - }; - - // Return all the elements for which a truth test fails. - _.reject = function(obj, predicate, context) { - return _.filter(obj, _.negate(lookupIterator(predicate)), context); - }; - - // Determine whether all of the elements match a truth test. - // Aliased as `all`. - _.every = _.all = function(obj, predicate, context) { - var result = true; - if (obj == null) return result; - predicate = lookupIterator(predicate, context); - _.each(obj, function(value, index, list) { - result = predicate(value, index, list); - if (!result) return breaker; - }); - return !!result; - }; - - // Determine if at least one element in the object matches a truth test. - // Aliased as `any`. - _.some = _.any = function(obj, predicate, context) { - var result = false; - if (obj == null) return result; - predicate = lookupIterator(predicate, context); - _.each(obj, function(value, index, list) { - result = predicate(value, index, list); - if (result) return breaker; - }); - return !!result; - }; - - // Determine if the array or object contains a given value (using `===`). - // Aliased as `include`. - _.contains = _.include = function(obj, target) { - if (obj == null) return false; - if (obj.length === +obj.length) return _.indexOf(obj, target) >= 0; - return _.some(obj, function(value) { - return value === target; - }); - }; - - // Invoke a method (with arguments) on every item in a collection. - _.invoke = function(obj, method) { - var args = slice.call(arguments, 2); - var isFunc = _.isFunction(method); - return _.map(obj, function(value) { - return (isFunc ? method : value[method]).apply(value, args); - }); - }; - - // Convenience version of a common use case of `map`: fetching a property. - _.pluck = function(obj, key) { - return _.map(obj, _.property(key)); - }; - - // Convenience version of a common use case of `filter`: selecting only objects - // containing specific `key:value` pairs. - _.where = function(obj, attrs) { - return _.filter(obj, _.matches(attrs)); - }; - - // Convenience version of a common use case of `find`: getting the first object - // containing specific `key:value` pairs. - _.findWhere = function(obj, attrs) { - return _.find(obj, _.matches(attrs)); - }; - - // Return the maximum element (or element-based computation). - _.max = function(obj, iterator, context) { - var result = -Infinity, lastComputed = -Infinity, - value, computed; - if (!iterator && _.isArray(obj)) { - for (var i = 0, length = obj.length; i < length; i++) { - value = obj[i]; - if (value > result) { - result = value; - } - } - } else { - iterator = lookupIterator(iterator, context); - _.each(obj, function(value, index, list) { - computed = iterator(value, index, list); - if (computed > lastComputed || computed === -Infinity && result === -Infinity) { - result = value; - lastComputed = computed; - } - }); - } - return result; - }; - - // Return the minimum element (or element-based computation). - _.min = function(obj, iterator, context) { - var result = Infinity, lastComputed = Infinity, - value, computed; - if (!iterator && _.isArray(obj)) { - for (var i = 0, length = obj.length; i < length; i++) { - value = obj[i]; - if (value < result) { - result = value; - } - } - } else { - iterator = lookupIterator(iterator, context); - _.each(obj, function(value, index, list) { - computed = iterator(value, index, list); - if (computed < lastComputed || computed === Infinity && result === Infinity) { - result = value; - lastComputed = computed; - } - }); - } - return result; - }; - - // Shuffle an array, using the modern version of the - // [Fisher-Yates shuffle](http://en.wikipedia.org/wiki/Fisher–Yates_shuffle). - _.shuffle = function(obj) { - var rand; - var index = 0; - var shuffled = []; - _.each(obj, function(value) { - rand = _.random(index++); - shuffled[index - 1] = shuffled[rand]; - shuffled[rand] = value; - }); - return shuffled; - }; - - // Sample **n** random values from a collection. - // If **n** is not specified, returns a single random element. - // The internal `guard` argument allows it to work with `map`. - _.sample = function(obj, n, guard) { - if (n == null || guard) { - if (obj.length !== +obj.length) obj = _.values(obj); - return obj[_.random(obj.length - 1)]; - } - return _.shuffle(obj).slice(0, Math.max(0, n)); - }; - - // Sort the object's values by a criterion produced by an iterator. - _.sortBy = function(obj, iterator, context) { - iterator = lookupIterator(iterator, context); - return _.pluck(_.map(obj, function(value, index, list) { - return { - value: value, - index: index, - criteria: iterator(value, index, list) - }; - }).sort(function(left, right) { - var a = left.criteria; - var b = right.criteria; - if (a !== b) { - if (a > b || a === void 0) return 1; - if (a < b || b === void 0) return -1; - } - return left.index - right.index; - }), 'value'); - }; - - // An internal function used for aggregate "group by" operations. - var group = function(behavior) { - return function(obj, iterator, context) { - var result = {}; - iterator = lookupIterator(iterator, context); - _.each(obj, function(value, index) { - var key = iterator(value, index, obj); - behavior(result, value, key); - }); - return result; - }; - }; - - // Groups the object's values by a criterion. Pass either a string attribute - // to group by, or a function that returns the criterion. - _.groupBy = group(function(result, value, key) { - if (_.has(result, key)) result[key].push(value); else result[key] = [value]; - }); - - // Indexes the object's values by a criterion, similar to `groupBy`, but for - // when you know that your index values will be unique. - _.indexBy = group(function(result, value, key) { - result[key] = value; - }); - - // Counts instances of an object that group by a certain criterion. Pass - // either a string attribute to count by, or a function that returns the - // criterion. - _.countBy = group(function(result, value, key) { - if (_.has(result, key)) result[key]++; else result[key] = 1; - }); - - // Use a comparator function to figure out the smallest index at which - // an object should be inserted so as to maintain order. Uses binary search. - _.sortedIndex = function(array, obj, iterator, context) { - iterator = lookupIterator(iterator, context, 1); - var value = iterator(obj); - var low = 0, high = array.length; - while (low < high) { - var mid = (low + high) >>> 1; - if (iterator(array[mid]) < value) low = mid + 1; else high = mid; - } - return low; - }; - - // Safely create a real, live array from anything iterable. - _.toArray = function(obj) { - if (!obj) return []; - if (_.isArray(obj)) return slice.call(obj); - if (obj.length === +obj.length) return _.map(obj, _.identity); - return _.values(obj); - }; - - // Return the number of elements in an object. - _.size = function(obj) { - if (obj == null) return 0; - return obj.length === +obj.length ? obj.length : _.keys(obj).length; - }; - - // Split a collection into two arrays: one whose elements all satisfy the given - // predicate, and one whose elements all do not satisfy the predicate. - _.partition = function(obj, predicate, context) { - predicate = lookupIterator(predicate, context, 1); - var pass = [], fail = []; - _.each(obj, function(value, key, obj) { - (predicate(value, key, obj) ? pass : fail).push(value); - }); - return [pass, fail]; - }; - - // Array Functions - // --------------- - - // Get the first element of an array. Passing **n** will return the first N - // values in the array. Aliased as `head` and `take`. The **guard** check - // allows it to work with `_.map`. - _.first = _.head = _.take = function(array, n, guard) { - if (array == null) return void 0; - if (n == null || guard) return array[0]; - if (n < 0) return []; - return slice.call(array, 0, n); - }; - - // Returns everything but the last entry of the array. Especially useful on - // the arguments object. Passing **n** will return all the values in - // the array, excluding the last N. The **guard** check allows it to work with - // `_.map`. - _.initial = function(array, n, guard) { - return slice.call(array, 0, Math.max(0, array.length - (n == null || guard ? 1 : n))); - }; - - // Get the last element of an array. Passing **n** will return the last N - // values in the array. The **guard** check allows it to work with `_.map`. - _.last = function(array, n, guard) { - if (array == null) return void 0; - if (n == null || guard) return array[array.length - 1]; - return slice.call(array, Math.max(array.length - n, 0)); - }; - - // Returns everything but the first entry of the array. Aliased as `tail` and `drop`. - // Especially useful on the arguments object. Passing an **n** will return - // the rest N values in the array. The **guard** - // check allows it to work with `_.map`. - _.rest = _.tail = _.drop = function(array, n, guard) { - return slice.call(array, n == null || guard ? 1 : n); - }; - - // Trim out all falsy values from an array. - _.compact = function(array) { - return _.filter(array, _.identity); - }; - - // Internal implementation of a recursive `flatten` function. - var flatten = function(input, shallow, strict, output) { - if (shallow && _.every(input, _.isArray)) { - return concat.apply(output, input); - } - for (var i = 0, length = input.length; i < length; i++) { - var value = input[i]; - if (!_.isArray(value) && !_.isArguments(value)) { - if (!strict) output.push(value); - } else if (shallow) { - push.apply(output, value); - } else { - flatten(value, shallow, strict, output); - } - } - return output; - }; - - // Flatten out an array, either recursively (by default), or just one level. - _.flatten = function(array, shallow) { - return flatten(array, shallow, false, []); - }; - - // Return a version of the array that does not contain the specified value(s). - _.without = function(array) { - return _.difference(array, slice.call(arguments, 1)); - }; - - // Produce a duplicate-free version of the array. If the array has already - // been sorted, you have the option of using a faster algorithm. - // Aliased as `unique`. - _.uniq = _.unique = function(array, isSorted, iterator, context) { - if (array == null) return []; - if (_.isFunction(isSorted)) { - context = iterator; - iterator = isSorted; - isSorted = false; - } - if (iterator) iterator = lookupIterator(iterator, context); - var result = []; - var seen = []; - for (var i = 0, length = array.length; i < length; i++) { - var value = array[i]; - if (iterator) value = iterator(value, i, array); - if (isSorted ? !i || seen !== value : !_.contains(seen, value)) { - if (isSorted) seen = value; - else seen.push(value); - result.push(array[i]); - } - } - return result; - }; - - // Produce an array that contains the union: each distinct element from all of - // the passed-in arrays. - _.union = function() { - return _.uniq(flatten(arguments, true, true, [])); - }; - - // Produce an array that contains every item shared between all the - // passed-in arrays. - _.intersection = function(array) { - if (array == null) return []; - var result = []; - var argsLength = arguments.length; - for (var i = 0, length = array.length; i < length; i++) { - var item = array[i]; - if (_.contains(result, item)) continue; - for (var j = 1; j < argsLength; j++) { - if (!_.contains(arguments[j], item)) break; - } - if (j === argsLength) result.push(item); - } - return result; - }; - - // Take the difference between one array and a number of other arrays. - // Only the elements present in just the first array will remain. - _.difference = function(array) { - var rest = flatten(slice.call(arguments, 1), true, true, []); - return _.filter(array, function(value){ - return !_.contains(rest, value); - }); - }; - - // Zip together multiple lists into a single array -- elements that share - // an index go together. - _.zip = function(array) { - if (array == null) return []; - var length = _.max(arguments, 'length').length; - var results = Array(length); - for (var i = 0; i < length; i++) { - results[i] = _.pluck(arguments, i); - } - return results; - }; - - // Converts lists into objects. Pass either a single array of `[key, value]` - // pairs, or two parallel arrays of the same length -- one of keys, and one of - // the corresponding values. - _.object = function(list, values) { - if (list == null) return {}; - var result = {}; - for (var i = 0, length = list.length; i < length; i++) { - if (values) { - result[list[i]] = values[i]; - } else { - result[list[i][0]] = list[i][1]; - } - } - return result; - }; - - // Return the position of the first occurrence of an item in an array, - // or -1 if the item is not included in the array. - // If the array is large and already in sort order, pass `true` - // for **isSorted** to use binary search. - _.indexOf = function(array, item, isSorted) { - if (array == null) return -1; - var i = 0, length = array.length; - if (isSorted) { - if (typeof isSorted == 'number') { - i = isSorted < 0 ? Math.max(0, length + isSorted) : isSorted; - } else { - i = _.sortedIndex(array, item); - return array[i] === item ? i : -1; - } - } - for (; i < length; i++) if (array[i] === item) return i; - return -1; - }; - - _.lastIndexOf = function(array, item, from) { - if (array == null) return -1; - var i = from == null ? array.length : from; - while (i--) if (array[i] === item) return i; - return -1; - }; - - // Generate an integer Array containing an arithmetic progression. A port of - // the native Python `range()` function. See - // [the Python documentation](http://docs.python.org/library/functions.html#range). - _.range = function(start, stop, step) { - if (arguments.length <= 1) { - stop = start || 0; - start = 0; - } - step = arguments[2] || 1; - - var length = Math.max(Math.ceil((stop - start) / step), 0); - var idx = 0; - var range = Array(length); - - while (idx < length) { - range[idx++] = start; - start += step; - } - - return range; - }; - - // Function (ahem) Functions - // ------------------ - - // Reusable constructor function for prototype setting. - var Ctor = function(){}; - - // Create a function bound to a given object (assigning `this`, and arguments, - // optionally). Delegates to **ECMAScript 5**'s native `Function.bind` if - // available. - _.bind = function(func, context) { - var args, bound; - if (nativeBind && func.bind === nativeBind) return nativeBind.apply(func, slice.call(arguments, 1)); - if (!_.isFunction(func)) throw TypeError('Bind must be called on a function'); - args = slice.call(arguments, 2); - bound = function() { - if (!(this instanceof bound)) return func.apply(context, args.concat(slice.call(arguments))); - Ctor.prototype = func.prototype; - var self = new Ctor; - Ctor.prototype = null; - var result = func.apply(self, args.concat(slice.call(arguments))); - if (Object(result) === result) return result; - return self; - }; - return bound; - }; - - // Partially apply a function by creating a version that has had some of its - // arguments pre-filled, without changing its dynamic `this` context. _ acts - // as a placeholder, allowing any combination of arguments to be pre-filled. - _.partial = function(func) { - var boundArgs = slice.call(arguments, 1); - return function() { - var position = 0; - var args = boundArgs.slice(); - for (var i = 0, length = args.length; i < length; i++) { - if (args[i] === _) args[i] = arguments[position++]; - } - while (position < arguments.length) args.push(arguments[position++]); - return func.apply(this, args); - }; - }; - - // Bind a number of an object's methods to that object. Remaining arguments - // are the method names to be bound. Useful for ensuring that all callbacks - // defined on an object belong to it. - _.bindAll = function(obj) { - var i = 1, length = arguments.length, key; - if (length <= 1) throw Error('bindAll must be passed function names'); - for (; i < length; i++) { - key = arguments[i]; - obj[key] = createCallback(obj[key], obj, Infinity); - } - return obj; - }; - - // Memoize an expensive function by storing its results. - _.memoize = function(func, hasher) { - var memoize = function(key) { - var cache = memoize.cache; - var address = hasher ? hasher.apply(this, arguments) : key; - if (!_.has(cache, address)) cache[address] = func.apply(this, arguments); - return cache[key]; - }; - memoize.cache = {}; - return memoize; - }; - - // Delays a function for the given number of milliseconds, and then calls - // it with the arguments supplied. - _.delay = function(func, wait) { - var args = slice.call(arguments, 2); - return setTimeout(function(){ - return func.apply(null, args); - }, wait); - }; - - // Defers a function, scheduling it to run after the current call stack has - // cleared. - _.defer = function(func) { - return _.delay.apply(_, [func, 1].concat(slice.call(arguments, 1))); - }; - - // Returns a function, that, when invoked, will only be triggered at most once - // during a given window of time. Normally, the throttled function will run - // as much as it can, without ever going more than once per `wait` duration; - // but if you'd like to disable the execution on the leading edge, pass - // `{leading: false}`. To disable execution on the trailing edge, ditto. - _.throttle = function(func, wait, options) { - var context, args, result; - var timeout = null; - var previous = 0; - if (!options) options = {}; - var later = function() { - previous = options.leading === false ? 0 : _.now(); - timeout = null; - result = func.apply(context, args); - if (!timeout) context = args = null; - }; - return function() { - var now = _.now(); - if (!previous && options.leading === false) previous = now; - var remaining = wait - (now - previous); - context = this; - args = arguments; - if (remaining <= 0 || remaining > wait) { - clearTimeout(timeout); - timeout = null; - previous = now; - result = func.apply(context, args); - if (!timeout) context = args = null; - } else if (!timeout && options.trailing !== false) { - timeout = setTimeout(later, remaining); - } - return result; - }; - }; - - // Returns a function, that, as long as it continues to be invoked, will not - // be triggered. The function will be called after it stops being called for - // N milliseconds. If `immediate` is passed, trigger the function on the - // leading edge, instead of the trailing. - _.debounce = function(func, wait, immediate) { - var timeout, args, context, timestamp, result; - - var later = function() { - var last = _.now() - timestamp; - - if (last < wait && last > 0) { - timeout = setTimeout(later, wait - last); - } else { - timeout = null; - if (!immediate) { - result = func.apply(context, args); - if (!timeout) context = args = null; - } - } - }; - - return function() { - context = this; - args = arguments; - timestamp = _.now(); - var callNow = immediate && !timeout; - if (!timeout) timeout = setTimeout(later, wait); - if (callNow) { - result = func.apply(context, args); - context = args = null; - } - - return result; - }; - }; - - // Returns a function that will be executed at most one time, no matter how - // often you call it. Useful for lazy initialization. - _.once = function(func) { - var ran = false, memo; - return function() { - if (ran) return memo; - ran = true; - memo = func.apply(this, arguments); - func = null; - return memo; - }; - }; - - // Returns the first function passed as an argument to the second, - // allowing you to adjust arguments, run code before and after, and - // conditionally execute the original function. - _.wrap = function(func, wrapper) { - return _.partial(wrapper, func); - }; - - // Returns a negated version of the passed-in predicate. - _.negate = function(predicate) { - return function() { - return !predicate.apply(this, arguments); - }; - }; - - // Returns a function that is the composition of a list of functions, each - // consuming the return value of the function that follows. - _.compose = function() { - var funcs = arguments; - return function() { - var args = arguments; - for (var i = funcs.length - 1; i >= 0; i--) { - args = [funcs[i].apply(this, args)]; - } - return args[0]; - }; - }; - - // Returns a function that will only be executed after being called N times. - _.after = function(times, func) { - return function() { - if (--times < 1) { - return func.apply(this, arguments); - } - }; - }; - - // Object Functions - // ---------------- - - // Retrieve the names of an object's properties. - // Delegates to **ECMAScript 5**'s native `Object.keys` - _.keys = function(obj) { - if (!_.isObject(obj)) return []; - if (nativeKeys) return nativeKeys(obj); - var keys = []; - for (var key in obj) if (_.has(obj, key)) keys.push(key); - return keys; - }; - - // Retrieve the values of an object's properties. - _.values = function(obj) { - var keys = _.keys(obj); - var length = keys.length; - var values = Array(length); - for (var i = 0; i < length; i++) { - values[i] = obj[keys[i]]; - } - return values; - }; - - // Convert an object into a list of `[key, value]` pairs. - _.pairs = function(obj) { - var keys = _.keys(obj); - var length = keys.length; - var pairs = Array(length); - for (var i = 0; i < length; i++) { - pairs[i] = [keys[i], obj[keys[i]]]; - } - return pairs; - }; - - // Invert the keys and values of an object. The values must be serializable. - _.invert = function(obj) { - var result = {}; - var keys = _.keys(obj); - for (var i = 0, length = keys.length; i < length; i++) { - result[obj[keys[i]]] = keys[i]; - } - return result; - }; - - // Return a sorted list of the function names available on the object. - // Aliased as `methods` - _.functions = _.methods = function(obj) { - var names = []; - for (var key in obj) { - if (_.isFunction(obj[key])) names.push(key); - } - return names.sort(); - }; - - // Extend a given object with all the properties in passed-in object(s). - _.extend = function(obj) { - if (!_.isObject(obj)) return obj; - var source, prop; - for (var i = 1, length = arguments.length; i < length; i++) { - source = arguments[i]; - for (prop in source) { - obj[prop] = source[prop]; - } - } - return obj; - }; - - // Return a copy of the object only containing the whitelisted properties. - _.pick = function(obj, iterator, context) { - var result = {}, key; - if (_.isFunction(iterator)) { - for (key in obj) { - var value = obj[key]; - if (iterator.call(context, value, key, obj)) result[key] = value; - } - } else { - var keys = concat.apply([], slice.call(arguments, 1)); - for (var i = 0, length = keys.length; i < length; i++) { - key = keys[i]; - if (key in obj) result[key] = obj[key]; - } - } - return result; - }; - - // Return a copy of the object without the blacklisted properties. - _.omit = function(obj, iterator, context) { - var keys; - if (_.isFunction(iterator)) { - iterator = _.negate(iterator); - } else { - keys = _.map(concat.apply([], slice.call(arguments, 1)), String); - iterator = function(value, key) { - return !_.contains(keys, key); - }; - } - return _.pick(obj, iterator, context); - }; - - // Fill in a given object with default properties. - _.defaults = function(obj) { - if (!_.isObject(obj)) return obj; - var source, prop; - for (var i = 1, length = arguments.length; i < length; i++) { - source = arguments[i]; - for (prop in source) { - if (obj[prop] === void 0) obj[prop] = source[prop]; - } - } - return obj; - }; - - // Create a (shallow-cloned) duplicate of an object. - _.clone = function(obj) { - if (!_.isObject(obj)) return obj; - return _.isArray(obj) ? obj.slice() : _.extend({}, obj); - }; - - // Invokes interceptor with the obj, and then returns obj. - // The primary purpose of this method is to "tap into" a method chain, in - // order to perform operations on intermediate results within the chain. - _.tap = function(obj, interceptor) { - interceptor(obj); - return obj; - }; - - // Internal recursive comparison function for `isEqual`. - var eq = function(a, b, aStack, bStack) { - // Identical objects are equal. `0 === -0`, but they aren't identical. - // See the [Harmony `egal` proposal](http://wiki.ecmascript.org/doku.php?id=harmony:egal). - if (a === b) return a !== 0 || 1 / a === 1 / b; - // A strict comparison is necessary because `null == undefined`. - if (a == null || b == null) return a === b; - // Unwrap any wrapped objects. - if (a instanceof _) a = a._wrapped; - if (b instanceof _) b = b._wrapped; - // Compare `[[Class]]` names. - var className = toString.call(a); - if (className !== toString.call(b)) return false; - switch (className) { - // Strings, numbers, regular expressions, dates, and booleans are compared by value. - case '[object RegExp]': - // RegExps are coerced to strings for comparison (Note: '' + /a/i === '/a/i') - case '[object String]': - // Primitives and their corresponding object wrappers are equivalent; thus, `"5"` is - // equivalent to `new String("5")`. - return '' + a === '' + b; - case '[object Number]': - // `NaN`s are equivalent, but non-reflexive. - // Object(NaN) is equivalent to NaN - if (a != +a) return b != +b; - // An `egal` comparison is performed for other numeric values. - return a == 0 ? 1 / a == 1 / b : a == +b; - case '[object Date]': - case '[object Boolean]': - // Coerce dates and booleans to numeric primitive values. Dates are compared by their - // millisecond representations. Note that invalid dates with millisecond representations - // of `NaN` are not equivalent. - return +a === +b; - } - if (typeof a != 'object' || typeof b != 'object') return false; - // Assume equality for cyclic structures. The algorithm for detecting cyclic - // structures is adapted from ES 5.1 section 15.12.3, abstract operation `JO`. - var length = aStack.length; - while (length--) { - // Linear search. Performance is inversely proportional to the number of - // unique nested structures. - if (aStack[length] === a) return bStack[length] === b; - } - // Objects with different constructors are not equivalent, but `Object`s - // from different frames are. - var aCtor = a.constructor, bCtor = b.constructor; - if ( - aCtor !== bCtor && - // Handle Object.create(x) cases - 'constructor' in a && 'constructor' in b && - !(_.isFunction(aCtor) && aCtor instanceof aCtor && - _.isFunction(bCtor) && bCtor instanceof bCtor) - ) { - return false; - } - // Add the first object to the stack of traversed objects. - aStack.push(a); - bStack.push(b); - var size, result; - // Recursively compare objects and arrays. - if (className === '[object Array]') { - // Compare array lengths to determine if a deep comparison is necessary. - size = a.length; - result = size === b.length; - if (result) { - // Deep compare the contents, ignoring non-numeric properties. - while (size--) { - if (!(result = eq(a[size], b[size], aStack, bStack))) break; - } - } - } else { - // Deep compare objects. - var keys = _.keys(a), key; - size = keys.length; - // Ensure that both objects contain the same number of properties before comparing deep equality. - result = _.keys(b).length == size; - if (result) { - while (size--) { - // Deep compare each member - key = keys[size]; - if (!(result = _.has(b, key) && eq(a[key], b[key], aStack, bStack))) break; - } - } - } - // Remove the first object from the stack of traversed objects. - aStack.pop(); - bStack.pop(); - return result; - }; - - // Perform a deep comparison to check if two objects are equal. - _.isEqual = function(a, b) { - return eq(a, b, [], []); - }; - - // Is a given array, string, or object empty? - // An "empty" object has no enumerable own-properties. - _.isEmpty = function(obj) { - if (obj == null) return true; - if (_.isArray(obj) || _.isString(obj) || _.isArguments(obj)) return obj.length === 0; - for (var key in obj) if (_.has(obj, key)) return false; - return true; - }; - - // Is a given value a DOM element? - _.isElement = function(obj) { - return !!(obj && obj.nodeType === 1); - }; - - // Is a given value an array? - // Delegates to ECMA5's native Array.isArray - _.isArray = nativeIsArray || function(obj) { - return toString.call(obj) === '[object Array]'; - }; - - // Is a given variable an object? - _.isObject = function(obj) { - return obj === Object(obj); - }; - - // Add some isType methods: isArguments, isFunction, isString, isNumber, isDate, isRegExp. - _.each(['Arguments', 'Function', 'String', 'Number', 'Date', 'RegExp'], function(name) { - _['is' + name] = function(obj) { - return toString.call(obj) === '[object ' + name + ']'; - }; - }); - - // Define a fallback version of the method in browsers (ahem, IE), where - // there isn't any inspectable "Arguments" type. - if (!_.isArguments(arguments)) { - _.isArguments = function(obj) { - return _.has(obj, 'callee'); - }; - } - - // Optimize `isFunction` if appropriate. - if (typeof /./ !== 'function') { - _.isFunction = function(obj) { - return typeof obj === 'function'; - }; - } - - // Is a given object a finite number? - _.isFinite = function(obj) { - return isFinite(obj) && !isNaN(parseFloat(obj)); - }; - - // Is the given value `NaN`? (NaN is the only number which does not equal itself). - _.isNaN = function(obj) { - return _.isNumber(obj) && obj !== +obj; - }; - - // Is a given value a boolean? - _.isBoolean = function(obj) { - return obj === true || obj === false || toString.call(obj) === '[object Boolean]'; - }; - - // Is a given value equal to null? - _.isNull = function(obj) { - return obj === null; - }; - - // Is a given variable undefined? - _.isUndefined = function(obj) { - return obj === void 0; - }; - - // Shortcut function for checking if an object has a given property directly - // on itself (in other words, not on a prototype). - _.has = function(obj, key) { - return obj != null && hasOwnProperty.call(obj, key); - }; - - // Utility Functions - // ----------------- - - // Run Underscore.js in *noConflict* mode, returning the `_` variable to its - // previous owner. Returns a reference to the Underscore object. - _.noConflict = function() { - root._ = previousUnderscore; - return this; - }; - - // Keep the identity function around for default iterators. - _.identity = function(value) { - return value; - }; - - _.constant = function(value) { - return function() { - return value; - }; - }; - - _.noop = function(){}; - - _.property = function(key) { - return function(obj) { - return obj[key]; - }; - }; - - // Returns a predicate for checking whether an object has a given set of `key:value` pairs. - _.matches = function(attrs) { - return function(obj) { - if (obj == null) return _.isEmpty(attrs); - if (obj === attrs) return true; - for (var key in attrs) if (attrs[key] !== obj[key]) return false; - return true; - }; - }; - - // Run a function **n** times. - _.times = function(n, iterator, context) { - var accum = Array(Math.max(0, n)); - iterator = createCallback(iterator, context, 1); - for (var i = 0; i < n; i++) accum[i] = iterator(i); - return accum; - }; - - // Return a random integer between min and max (inclusive). - _.random = function(min, max) { - if (max == null) { - max = min; - min = 0; - } - return min + Math.floor(Math.random() * (max - min + 1)); - }; - - // A (possibly faster) way to get the current timestamp as an integer. - _.now = Date.now || function() { - return new Date().getTime(); - }; - - // List of HTML entities for escaping. - var entityMap = { - escape: { - '&': '&', - '<': '<', - '>': '>', - '"': '"', - "'": ''' - } - }; - entityMap.unescape = _.invert(entityMap.escape); - - // Regexes containing the keys and values listed immediately above. - var entityRegexes = { - escape: RegExp('[' + _.keys(entityMap.escape).join('') + ']', 'g'), - unescape: RegExp('(' + _.keys(entityMap.unescape).join('|') + ')', 'g') - }; - - // Functions for escaping and unescaping strings to/from HTML interpolation. - _.each(['escape', 'unescape'], function(method) { - _[method] = function(string) { - if (string == null) return ''; - return ('' + string).replace(entityRegexes[method], function(match) { - return entityMap[method][match]; - }); - }; - }); - - // If the value of the named `property` is a function then invoke it with the - // `object` as context; otherwise, return it. - _.result = function(object, property) { - if (object == null) return void 0; - var value = object[property]; - return _.isFunction(value) ? object[property]() : value; - }; - - // Generate a unique integer id (unique within the entire client session). - // Useful for temporary DOM ids. - var idCounter = 0; - _.uniqueId = function(prefix) { - var id = ++idCounter + ''; - return prefix ? prefix + id : id; - }; - - // By default, Underscore uses ERB-style template delimiters, change the - // following template settings to use alternative delimiters. - _.templateSettings = { - evaluate : /<%([\s\S]+?)%>/g, - interpolate : /<%=([\s\S]+?)%>/g, - escape : /<%-([\s\S]+?)%>/g - }; - - // When customizing `templateSettings`, if you don't want to define an - // interpolation, evaluation or escaping regex, we need one that is - // guaranteed not to match. - var noMatch = /(.)^/; - - // Certain characters need to be escaped so that they can be put into a - // string literal. - var escapes = { - "'": "'", - '\\': '\\', - '\r': 'r', - '\n': 'n', - '\u2028': 'u2028', - '\u2029': 'u2029' - }; - - var escaper = /\\|'|\r|\n|\u2028|\u2029/g; - - var escapeChar = function(match) { - return '\\' + escapes[match]; - }; - - // JavaScript micro-templating, similar to John Resig's implementation. - // Underscore templating handles arbitrary delimiters, preserves whitespace, - // and correctly escapes quotes within interpolated code. - _.template = function(text, data, settings) { - settings = _.defaults({}, settings, _.templateSettings); - - // Combine delimiters into one regular expression via alternation. - var matcher = RegExp([ - (settings.escape || noMatch).source, - (settings.interpolate || noMatch).source, - (settings.evaluate || noMatch).source - ].join('|') + '|$', 'g'); - - // Compile the template source, escaping string literals appropriately. - var index = 0; - var source = "__p+='"; - text.replace(matcher, function(match, escape, interpolate, evaluate, offset) { - source += text.slice(index, offset).replace(escaper, escapeChar); - index = offset + match.length; - - if (escape) { - source += "'+\n((__t=(" + escape + "))==null?'':_.escape(__t))+\n'"; - } else if (interpolate) { - source += "'+\n((__t=(" + interpolate + "))==null?'':__t)+\n'"; - } else if (evaluate) { - source += "';\n" + evaluate + "\n__p+='"; - } - - // Adobe VMs need the match returned to produce the correct offest. - return match; - }); - source += "';\n"; - - // If a variable is not specified, place data values in local scope. - if (!settings.variable) source = 'with(obj||{}){\n' + source + '}\n'; - - source = "var __t,__p='',__j=Array.prototype.join," + - "print=function(){__p+=__j.call(arguments,'');};\n" + - source + 'return __p;\n'; - - try { - var render = Function(settings.variable || 'obj', '_', source); - } catch (e) { - e.source = source; - throw e; - } - - if (data) return render(data, _); - var template = function(data) { - return render.call(this, data, _); - }; - - // Provide the compiled source as a convenience for precompilation. - var argument = settings.variable || 'obj'; - template.source = 'function(' + argument + '){\n' + source + '}'; - - return template; - }; - - // Add a "chain" function, which will delegate to the wrapper. - _.chain = function(obj) { - return _(obj).chain(); - }; - - // OOP - // --------------- - // If Underscore is called as a function, it returns a wrapped object that - // can be used OO-style. This wrapper holds altered versions of all the - // underscore functions. Wrapped objects may be chained. - - // Helper function to continue chaining intermediate results. - var result = function(obj) { - return this._chain ? _(obj).chain() : obj; - }; - - // Add your own custom functions to the Underscore object. - _.mixin = function(obj) { - _.each(_.functions(obj), function(name) { - var func = _[name] = obj[name]; - _.prototype[name] = function() { - var args = [this._wrapped]; - push.apply(args, arguments); - return result.call(this, func.apply(_, args)); - }; - }); - }; - - // Add all of the Underscore functions to the wrapper object. - _.mixin(_); - - // Add all mutator Array functions to the wrapper. - _.each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(name) { - var method = ArrayProto[name]; - _.prototype[name] = function() { - var obj = this._wrapped; - method.apply(obj, arguments); - if ((name === 'shift' || name === 'splice') && obj.length === 0) delete obj[0]; - return result.call(this, obj); - }; - }); - - // Add all accessor Array functions to the wrapper. - _.each(['concat', 'join', 'slice'], function(name) { - var method = ArrayProto[name]; - _.prototype[name] = function() { - return result.call(this, method.apply(this._wrapped, arguments)); - }; - }); - - _.extend(_.prototype, { - - // Start chaining a wrapped Underscore object. - chain: function() { - this._chain = true; - return this; - }, - - // Extracts the result from a wrapped and chained object. - value: function() { - return this._wrapped; - } - - }); - - // AMD registration happens at the end for compatibility with AMD loaders - // that may not enforce next-turn semantics on modules. Even though general - // practice for AMD registration is to be anonymous, underscore registers - // as a named module because, like jQuery, it is a base library that is - // popular enough to be bundled in a third party lib, but not be part of - // an AMD load request. Those cases could generate an error when an - // anonymous define() is called outside of a loader request. - if (typeof define === 'function' && define.amd) { - define('underscore', [], function() { - return _; - }); - } -}.call(this)); From f829969272db3c7d2ab3b06c76592a242f61e10f Mon Sep 17 00:00:00 2001 From: Pedro Garcia Date: Mon, 30 Jun 2014 09:38:41 +0200 Subject: [PATCH 195/823] new CanvasLayer.js --- .../map/views/layers/UMDLossLayerView.js | 4 +- .../map/views/layers/class/CanvasLayer.js | 226 +++++++++--------- .../javascripts/map/views/layers/forest.js | 2 +- 3 files changed, 118 insertions(+), 114 deletions(-) diff --git a/app/assets/javascripts/map/views/layers/UMDLossLayerView.js b/app/assets/javascripts/map/views/layers/UMDLossLayerView.js index 677acd9c3e..737a581617 100644 --- a/app/assets/javascripts/map/views/layers/UMDLossLayerView.js +++ b/app/assets/javascripts/map/views/layers/UMDLossLayerView.js @@ -23,7 +23,7 @@ define([ }, /** - * Implementation for CanvasLayerView.filterCanvasImage(). + * Implementation for CanvasLayerView.filterCanvasImgdata(). * * @param {[type]} imgdata [description] * @param {[type]} w [description] @@ -31,7 +31,7 @@ define([ * @param {[type]} z [description] * @return {[type]} [description] */ - filterCanvasImage: function(imgdata, w, h, z) { + filterCanvasImgdata: function(imgdata, w, h, z) { var components = 4; var timelineDate = [this.timelineDate[0].year(), this.timelineDate[1].year()]; diff --git a/app/assets/javascripts/map/views/layers/class/CanvasLayer.js b/app/assets/javascripts/map/views/layers/class/CanvasLayer.js index 2c1afdcb2a..f088725d0d 100644 --- a/app/assets/javascripts/map/views/layers/class/CanvasLayer.js +++ b/app/assets/javascripts/map/views/layers/class/CanvasLayer.js @@ -5,12 +5,15 @@ */ define([ 'Class', - 'uri' -], function(Class, UriTemplate) { + 'underscore' +], function(Class, _) { + + 'use strict'; var CanvasLayer = Class.extend({ init: function () { + _.bindAll(this, 'filterCanvasImgdata'); this.tileSize = new google.maps.Size(256, 256); this.tiles = {}; }, @@ -19,76 +22,102 @@ define([ * Called whenever the Google Maps API determines that the map needs to * display new tiles for the given viewport. * - * @param {[type]} coord [description] - * @param {[type]} zoom [description] - * @param {[type]} ownerDocument [description] - * @return {[type]} [description] + * @param {obj} coord coordenades {x ,y} + * @param {integer} zoom current map zoom + * @param {object} ownerDocument + * + * @return {canvas} canvas tile canvas */ getTile: function(coord, zoom, ownerDocument) { - var canvas = ownerDocument.createElement('canvas'); - canvas.style.border = "none"; - canvas.style.margin = "0"; - canvas.style.padding = "0"; - - var ctx = canvas.getContext('2d'); - ctx.width = canvas.width = this.tileSize.width; - ctx.height = canvas.height = this.tileSize.height; + var tileId = this._getTileId(coord.x, coord.y, zoom); + // Delete all tiles from others zooms; + var tilesKeys = Object.keys(this.tiles); - var tileId = coord.x + '_' + coord.y + '_' + zoom; - canvas.setAttribute('id', tileId); + for (var i = 0; i < tilesKeys.length; i++) { + if (this.tiles[tilesKeys[i]].z !== zoom) { + delete this.tiles[tilesKeys[i]]; + } + }; - if (tileId in this.tiles) { - delete this.tiles[tileId]; + // Return cached tile if loaded. + if (this.tiles[tileId]) { + return this.tiles[tileId].canvas; } - this.tiles[tileId] = canvas; - this.canvasSetup(canvas, coord, zoom); + var canvas = ownerDocument.createElement('canvas'); + canvas.style.border = 'none'; + canvas.style.margin = '0'; + canvas.style.padding = '0'; + canvas.width = this.tileSize.width; + canvas.height = this.tileSize.height; + + var url = this._getUrl.apply(this, + this._getTileCoords(coord.x, coord.y, zoom)); + + this._getImage(url, _.bind(function(image) { + var canvasData = { + tileId: tileId, + canvas: canvas, + image: image, + x: coord.x, + y: coord.y, + z: zoom + }; + + this._cacheTile(canvasData); + this._drawCanvasImage(canvasData); + }, this)); return canvas; }, - canvasSetup: function(canvas, coord, zoom) { - var self = this, - xhr = new XMLHttpRequest(), - ctx = canvas.getContext('2d'); + _drawCanvasImage: function(canvasData) { + var canvas = canvasData.canvas; + var image = canvasData.image; + var x = canvasData.x; + var y = canvasData.y; + var z = canvasData.z; - var x = coord.x, - y = coord.y, - z = zoom; + var ctx = canvas.getContext('2d'); + var zsteps = this._getZoomSteps(z); - if (zoom > this.dataMaxZoom) { - x = Math.floor(coord.x / (Math.pow(2, zoom - this.dataMaxZoom))); - y = Math.floor(coord.y / (Math.pow(2, zoom - this.dataMaxZoom))); - z = this.dataMaxZoom; + if (zsteps < 0) { + ctx.drawImage(image, 0, 0); } else { - y = (y > Math.pow(2, z) ? y % Math.pow(2, z) : y); - if (x >= Math.pow(2, z)) { - x = x % Math.pow(2, z); - } else if (x < 0) { - x = Math.pow(2, z) - Math.abs(x); - } + ctx.imageSmoothingEnabled = false; + ctx.mozImageSmoothingEnabled = false; + ctx.webkitImageSmoothingEnabled = false; + + var srcX = 256 / Math.pow(2, zsteps) * (x % Math.pow(2, zsteps)); + var srcY = 256 / Math.pow(2, zsteps) * (y % Math.pow(2, zsteps)); + var srcW = 256 / Math.pow(2, zsteps); + var srcH = 256 / Math.pow(2, zsteps); + + ctx.clearRect(0, 0, 256, 256); + ctx.drawImage(image, srcX, srcY, srcW, srcH, 0, 0, 256, 256); } - var params = {z: z, x: x, y: y}; - var url = new UriTemplate(this._urlTemplate).fillFromObject(urlParams); + var I = ctx.getImageData(0, 0, canvas.width, canvas.height); + this.filterCanvasImgdata(I.data, canvas.width, canvas.height, z); + ctx.putImageData(I, 0, 0); + }, + + _getZoomSteps: function(z) { + return z - this.dataMaxZoom; + }, + + _getImage: function(url, callback) { + var xhr = new XMLHttpRequest(); xhr.onload = function () { - var url = URL.createObjectURL(this.response), - image = new Image(); + var url = URL.createObjectURL(this.response); + var image = new Image(); image.onload = function () { image.crossOrigin = ''; - - canvas.image = image; - canvas.coord = coord; - canvas.coord.z = zoom; - - ctx.drawImage(image, 0, 0); - self.filterTile(canvas, zoom); - + callback(image); URL.revokeObjectURL(url); }; - image.src = url; }; @@ -97,85 +126,60 @@ define([ xhr.send(); }, - /** - * Filters the canvas image. Subclasses implement this. - * - * @param {object} imgdata - * @param {integer} w width - * @param {integer} h height - * @param {integer} zoom - */ - filterCanvasImage: function(imgdata, w, h, zoom) { - // NOOP + _getUrl: function(x, y, z) { + return this.urlTemplate.replace('%z', z).replace('%x', x).replace('%y', y); }, - /** - * Update current tiles by calling this.filterTile(). - */ - updateTiles: function() { - for(var i in this.tiles) { - this.filterTile(this.tiles[i]); + _getTileCoords: function(x, y, z) { + if (z > this.dataMaxZoom) { + x = Math.floor(x / (Math.pow(2, z - this.dataMaxZoom))); + y = Math.floor(y / (Math.pow(2, z - this.dataMaxZoom))); + z = this.dataMaxZoom; + } else { + y = (y > Math.pow(2, z) ? y % Math.pow(2, z) : y); + if (x >= Math.pow(2, z)) { + x = x % Math.pow(2, z); + } else if (x < 0) { + x = Math.pow(2, z) - Math.abs(x); + } } + + return [x, y, z]; }, /** - * Filter canvas tile. + * Caches a tile so it can be re-rendered when + * calling this.updateTiles() * - * @param {canvas} canvas - * @param {integer} zoom + * @param {object} canvasData Tile canvas data */ - filterTile: function(canvas, zoom) { - var ctx = canvas.getContext('2d'); - coord = canvas.coord; - - if (canvas.coord) { - var zsteps = coord.z - this.dataMaxZoom; - - if (zsteps > 0) { - ctx.imageSmoothingEnabled = false; - ctx.mozImageSmoothingEnabled = false; - ctx.webkitImageSmoothingEnabled = false; - - var srcX = 256 / Math.pow(2, zsteps) * (coord.x % Math.pow(2, zsteps)), - srcY = 256 / Math.pow(2, zsteps) * (coord.y % Math.pow(2, zsteps)), - srcW = 256 / Math.pow(2, zsteps), - srcH = 256 / Math.pow(2, zsteps); - - ctx.clearRect(0, 0, 256, 256); - ctx.drawImage(canvas.image, srcX, srcY, srcW, srcH, 0, 0, 256, 256); - } else { - try { - ctx.drawImage(canvas.image, 0, 0); - } catch(err) { } - } - - var I = ctx.getImageData(0, 0, canvas.width, canvas.height); - this.filterCanvasImage(I.data, ctx.width, ctx.height, zoom); - ctx.putImageData(I,0,0); - } + _cacheTile: function(canvasData) { + canvasData.canvas.setAttribute('id', canvasData.tileId); + this.tiles[tileId] = canvasData; }, - /** - * Return the layer - */ - getLayer: function() { - return this.layer; + _getTileId: function(x, y, z) { + return x + '_' + y + '_' + z; }, - /** - * Return the view name - */ - getName: function() { - return this.layer.name; + updateTiles: function() { + for(var i in this.tiles) { + this._drawCanvasImage(this.tiles[i]); + } }, /** - * Return the layer category slug + * Filters the canvas imagedata. Subclasses implement this. + * + * @param {object} imgdata + * @param {integer} w width + * @param {integer} h height + * @param {integer} zoom */ - getCategory: function() { - return this.layer.category_slug; + filterCanvasImgdata: function(imgdata, w, h, zoom) { } }); return CanvasLayer; + }); diff --git a/app/assets/javascripts/map/views/layers/forest.js b/app/assets/javascripts/map/views/layers/forest.js index 31b583cb1f..1a8964e467 100644 --- a/app/assets/javascripts/map/views/layers/forest.js +++ b/app/assets/javascripts/map/views/layers/forest.js @@ -22,7 +22,7 @@ define([ ForestLayer.__super__.initialize.apply(this); }, - filterCanvasImage: function(imgdata, w, h) { + filterCanvasImgdata: function(imgdata, w, h) { var components = 4, pixelPos, intensity; From 7fca8898c3065d2477538d7e4dc5a3e98b89c79d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A1n=20P=C3=A9rez?= Date: Mon, 30 Jun 2014 12:01:39 +0200 Subject: [PATCH 196/823] analysis protected area: show analysis box forest use --- lib/assets/javascripts/gfw/ui/analysis.js.erb | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/assets/javascripts/gfw/ui/analysis.js.erb b/lib/assets/javascripts/gfw/ui/analysis.js.erb index e96eee407f..bc2f08193b 100644 --- a/lib/assets/javascripts/gfw/ui/analysis.js.erb +++ b/lib/assets/javascripts/gfw/ui/analysis.js.erb @@ -461,11 +461,15 @@ gfw.ui.view.Analysis = cdb.core.View.extend({ } if (this.$el.find('.no_data_available').length > 0) this.$el.find('.no_data_available').remove(); if (no_data) { - debugger; + this.info.model.set({ + 'spinner': false + }); this.info.render(); - this.$el.find('.stats .spinner').fadeOut(150, function() { + this.$el.find('.stats .spinner').fadeOut(250, function() { + that.$el.find('.stats .title, .stats ul').fadeIn(250); that.$el.find('.analysis_info .stats').append('

    No data available

    ').fadeIn(150); }); + debugger; this.info.setDraggable(true); } else { executeAjax(this.alertsUrl, {}, { From a604df28c52e094245da35161bccc70c5981be20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A1n=20P=C3=A9rez?= Date: Mon, 30 Jun 2014 12:18:33 +0200 Subject: [PATCH 197/823] conservation and forest use: show analysis box when there is no data --- lib/assets/javascripts/gfw/ui/analysis.js.erb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/assets/javascripts/gfw/ui/analysis.js.erb b/lib/assets/javascripts/gfw/ui/analysis.js.erb index bc2f08193b..c7647502d6 100644 --- a/lib/assets/javascripts/gfw/ui/analysis.js.erb +++ b/lib/assets/javascripts/gfw/ui/analysis.js.erb @@ -465,11 +465,10 @@ gfw.ui.view.Analysis = cdb.core.View.extend({ 'spinner': false }); this.info.render(); + this.$el.show(); this.$el.find('.stats .spinner').fadeOut(250, function() { - that.$el.find('.stats .title, .stats ul').fadeIn(250); that.$el.find('.analysis_info .stats').append('

    No data available

    ').fadeIn(150); }); - debugger; this.info.setDraggable(true); } else { executeAjax(this.alertsUrl, {}, { From 1b640da640b01b029c836cf5fba1dad8eccd4c0c Mon Sep 17 00:00:00 2001 From: Pedro Garcia Date: Mon, 30 Jun 2014 16:59:43 +0200 Subject: [PATCH 198/823] layers-nav-presenter --- .../map/presenters/LayersNavPresenter.js | 44 +++++++++++++++++++ .../map/presenters/MapPresenter.js | 6 +-- .../map/presenters/TimelinePresenter.js | 4 +- .../javascripts/map/services/PlaceService.js | 16 +++---- app/assets/javascripts/map/views/MapView.js | 9 ++-- .../map/views/layers/UMDLossLayerView.js | 2 +- .../map/views/layers/class/CanvasLayer.js | 4 +- .../javascripts/map/views/layersNavView.js | 34 +++++++++++--- app/assets/javascripts/map/views/timeline.js | 4 ++ 9 files changed, 96 insertions(+), 27 deletions(-) diff --git a/app/assets/javascripts/map/presenters/LayersNavPresenter.js b/app/assets/javascripts/map/presenters/LayersNavPresenter.js index e69de29bb2..6b6cd7a346 100644 --- a/app/assets/javascripts/map/presenters/LayersNavPresenter.js +++ b/app/assets/javascripts/map/presenters/LayersNavPresenter.js @@ -0,0 +1,44 @@ +/** + * The LayersNavPresenter class for the LayersNavView. + * + * @return LayersNavPresenter class. + */ +define([ + 'Class', + 'underscore', + 'mps' +], function(Class, _, mps) { + + 'use strict'; + + var LayersNavPresenter = Class.extend({ + + /** + * Initialize LayersNavPresenter. + * + * @param {object} Instance of LayersNavView + */ + init: function(view) { + this.view = view; + this._subscribe(); + }, + + /** + * Subscribe to application events. + */ + _subscribe: function() { + mps.subscribe('Map/toggle-layer', this.view._toggleSelected); + }, + + /** + * Publish a a Map/toggle-layer. + * + * @param {string} layerName + */ + toggleLayer: function(layerName) { + mps.publish('Map/toggle-layer', [layerName]); + } + }); + + return LayersNavPresenter; +}); diff --git a/app/assets/javascripts/map/presenters/MapPresenter.js b/app/assets/javascripts/map/presenters/MapPresenter.js index 36cf88dcef..aa28fb167f 100644 --- a/app/assets/javascripts/map/presenters/MapPresenter.js +++ b/app/assets/javascripts/map/presenters/MapPresenter.js @@ -39,7 +39,7 @@ define([ this._initMap(place.params); this._initLayers(this.layers); } - }, this)); + }, this)); mps.publish('Place/register', [this]); }, @@ -65,9 +65,9 @@ define([ }, /** - * Retuns place parameters representing the state of the MapView and + * Retuns place parameters representing the state of the MapView and * layers. Called by PlaceService. - * + * * @return {Object} Params representing the state of the MapView and layers */ getPlaceParams: function() { diff --git a/app/assets/javascripts/map/presenters/TimelinePresenter.js b/app/assets/javascripts/map/presenters/TimelinePresenter.js index fbbff9f319..aca89306e5 100644 --- a/app/assets/javascripts/map/presenters/TimelinePresenter.js +++ b/app/assets/javascripts/map/presenters/TimelinePresenter.js @@ -25,7 +25,7 @@ define([ mps.subscribe('Place/go', _.bind(function(place) { // if (place.name === 'map') { // } - }, this)); + }, this)); mps.publish('Place/register', [this]); }, @@ -38,7 +38,7 @@ define([ * @param {Array} date 2D array of moment dates [begin, end] */ updateTimelineDate: function(date) { - mps.publish('Timeline/date-change', [date]); + mps.publish('Timeline/date-change', [this.view.getLayerName(), date]); mps.publish('Place/update', [{go: false}]); } diff --git a/app/assets/javascripts/map/services/PlaceService.js b/app/assets/javascripts/map/services/PlaceService.js index 2c87b920f4..2361eedff2 100644 --- a/app/assets/javascripts/map/services/PlaceService.js +++ b/app/assets/javascripts/map/services/PlaceService.js @@ -31,7 +31,7 @@ * 2) Updated view updates URL * * A View state changes (e.g., a new map zoom) and the URL needs to be - * updated, not only with its new state, but from the stae of all views in + * updated, not only with its new state, but from the state of all views in * the application that provide state for URLs. * * Here presenters publishe the "Place/register" event passing in a @@ -63,7 +63,7 @@ define([ * * @param {MapLayerService} mapLayerService Instance of MapLayerService * @param {Backbond.Router} router Instance of Backbone.Router - * @return {PlaceService} Instance of PlaceService + * @return {PlaceService} Instance of PlaceService */ init: function(mapLayerService, router) { this.mapLayerService = mapLayerService; @@ -92,9 +92,9 @@ define([ * which will include layers retrieved from the MapLayerService. Otherwise * the URL is silently updated with a new route. * - * @param {string} name The place name - * @param {Object} params The place parameters - * @param {[type]} go True to publish Place/go event, false to update URL + * @param {string} name The place name + * @param {Object} params The place parameters + * @param {boolean} go True to publish Place/go event, false to update URL */ _handleNewPlace: function(name, params, go) { var route = null; @@ -122,10 +122,10 @@ define([ }, /** - * Return formated URL representation of supplied params object based on + * Return formated URL representation of supplied params object based on * a route name. * - * @param {string} name The route name + * @param {string} name The route name * @param {Object} params Params to standardize * @return {Object} Params ready for URL */ @@ -142,7 +142,7 @@ define([ /** * Return route URL for supplied route name and route params. - * + * * @param {string} name The route name (e.g. map) * @param {Object} params The route params * @return {string} The route URL diff --git a/app/assets/javascripts/map/views/MapView.js b/app/assets/javascripts/map/views/MapView.js index 375f045286..cf9f4b16fc 100644 --- a/app/assets/javascripts/map/views/MapView.js +++ b/app/assets/javascripts/map/views/MapView.js @@ -67,7 +67,7 @@ define([ }; this.render(options); }, - + /** * Used by MapPresenter to initialize the map view. This function clears * all layers from the map and then adds supplied layers in order. @@ -104,12 +104,13 @@ define([ addLayer: function(layer) { var layerView = null; - if (layer.slug === 'loss') { - if (!_.has(this.layerViews, 'loss')) { + if (layer.slug === 'umd_tree_loss_gain') { + if (!_.has(this.layerViews, layer.slug)) { layerView = new UMDLossLayerView(layer); - this.layerViews.loss = layerView; + this.layerViews[layer.slug] = layerView; } } + this.map.overlayMapTypes.insertAt(0, layerView); }, diff --git a/app/assets/javascripts/map/views/layers/UMDLossLayerView.js b/app/assets/javascripts/map/views/layers/UMDLossLayerView.js index 677acd9c3e..1b82e84029 100644 --- a/app/assets/javascripts/map/views/layers/UMDLossLayerView.js +++ b/app/assets/javascripts/map/views/layers/UMDLossLayerView.js @@ -34,7 +34,7 @@ define([ filterCanvasImage: function(imgdata, w, h, z) { var components = 4; - var timelineDate = [this.timelineDate[0].year(), this.timelineDate[1].year()]; + var timelineDate = [2001, 2004]; for(var i = 0; i < w; ++i) { for(var j = 0; j < h; ++j) { diff --git a/app/assets/javascripts/map/views/layers/class/CanvasLayer.js b/app/assets/javascripts/map/views/layers/class/CanvasLayer.js index 2c1afdcb2a..ce439f8e3f 100644 --- a/app/assets/javascripts/map/views/layers/class/CanvasLayer.js +++ b/app/assets/javascripts/map/views/layers/class/CanvasLayer.js @@ -69,8 +69,8 @@ define([ } } - var params = {z: z, x: x, y: y}; - var url = new UriTemplate(this._urlTemplate).fillFromObject(urlParams); + var urlParams = {z: z, x: x, y: y}; + var url = new UriTemplate(this.urlTemplate).fillFromObject(urlParams); xhr.onload = function () { var url = URL.createObjectURL(this.response), diff --git a/app/assets/javascripts/map/views/layersNavView.js b/app/assets/javascripts/map/views/layersNavView.js index 922bebfaa5..23f953d943 100644 --- a/app/assets/javascripts/map/views/layersNavView.js +++ b/app/assets/javascripts/map/views/layersNavView.js @@ -6,22 +6,24 @@ define([ 'backbone', 'underscore', + 'presenters/LayersNavPresenter', 'text!templates/layersNav.html' -], function(Backbone, _, layersNavTpl) { +], function(Backbone, _, Presenter, tpl) { 'use strict'; var LayersNavView = Backbone.View.extend({ el: '.layers-menu', - template: _.template(layersNavTpl), + template: _.template(tpl), events: { - 'click .layer-title': 'onClickLayer', - 'click .radio': 'onClickLayer' + 'click .layer-title': '_toggleLayer' }, initialize: function() { + _.bindAll(this, '_toggleSelected'); + this.presenter = new Presenter(this); this.render(); }, @@ -29,11 +31,29 @@ define([ this.$el.append(this.template()); }, - onClickLayer: function(event) { - var layerName = $(event.currentTarget).parents('li').data('layer'); + /** + * Used by LayersNavPresenter to toggle the selected class name + * on a layer. + * + * @param {string} layerName + */ + _toggleSelected: function(layerName) { + this.$el.find('li[data-layer="' + layerName + '"]') + .toggleClass('selected'); + }, + + /** + * Handles a toggle layer change UI event by dispatching + * to LayersNavPresenter. + * + * @param {event} event Click event + */ + _toggleLayer: function(event) { + var layerName = $(event.currentTarget).parents('li') + .data('layer'); if (layerName) { - window.mps.publish('presenter/toggle-layer', [layerName]); + this.presenter.toggleLayer(layerName) } }, diff --git a/app/assets/javascripts/map/views/timeline.js b/app/assets/javascripts/map/views/timeline.js index 5ea35486da..4ebbcc62dc 100644 --- a/app/assets/javascripts/map/views/timeline.js +++ b/app/assets/javascripts/map/views/timeline.js @@ -418,6 +418,10 @@ define([ hideTipsy: function() { this.tipsy.style('visibility', 'hidden'); this.tooltip.style('visibility', 'hidden'); + }, + + getLayerName: function() { + return this.opts.layerName; } }); From 7add6089705997e7a067496a6b848886d1e2e4b9 Mon Sep 17 00:00:00 2001 From: Aaron Date: Mon, 30 Jun 2014 09:16:55 -0700 Subject: [PATCH 199/823] updating tests --- jstest/spec/UMDLossLayerPresenter_spec.js | 2 +- jstest/{spec => staging}/AnalysisButtonPresenter_spec.js | 0 jstest/{spec => staging}/AnalysisService_spec.js | 0 jstest/{spec => staging}/app_spec.js | 0 jstest/{spec => staging}/foo_spec.js | 0 jstest/{spec => staging}/nsa_spec.js | 0 jstest/{spec => staging}/router_spec.js | 0 7 files changed, 1 insertion(+), 1 deletion(-) rename jstest/{spec => staging}/AnalysisButtonPresenter_spec.js (100%) rename jstest/{spec => staging}/AnalysisService_spec.js (100%) rename jstest/{spec => staging}/app_spec.js (100%) rename jstest/{spec => staging}/foo_spec.js (100%) rename jstest/{spec => staging}/nsa_spec.js (100%) rename jstest/{spec => staging}/router_spec.js (100%) diff --git a/jstest/spec/UMDLossLayerPresenter_spec.js b/jstest/spec/UMDLossLayerPresenter_spec.js index 078ac48c05..89a7a700cc 100644 --- a/jstest/spec/UMDLossLayerPresenter_spec.js +++ b/jstest/spec/UMDLossLayerPresenter_spec.js @@ -31,7 +31,7 @@ define([ }); it("Check Timeline/change event handling", function() { - mps.publish('Timeline/change', ['loss', [2001, 2002]]); + mps.publish('Timeline/date-change', ['loss', [2001, 2002]]); expect(viewSpy.setTimelineDate).toHaveBeenCalled(); expect(viewSpy.setTimelineDate).toHaveBeenCalledWith([2001, 2002]); diff --git a/jstest/spec/AnalysisButtonPresenter_spec.js b/jstest/staging/AnalysisButtonPresenter_spec.js similarity index 100% rename from jstest/spec/AnalysisButtonPresenter_spec.js rename to jstest/staging/AnalysisButtonPresenter_spec.js diff --git a/jstest/spec/AnalysisService_spec.js b/jstest/staging/AnalysisService_spec.js similarity index 100% rename from jstest/spec/AnalysisService_spec.js rename to jstest/staging/AnalysisService_spec.js diff --git a/jstest/spec/app_spec.js b/jstest/staging/app_spec.js similarity index 100% rename from jstest/spec/app_spec.js rename to jstest/staging/app_spec.js diff --git a/jstest/spec/foo_spec.js b/jstest/staging/foo_spec.js similarity index 100% rename from jstest/spec/foo_spec.js rename to jstest/staging/foo_spec.js diff --git a/jstest/spec/nsa_spec.js b/jstest/staging/nsa_spec.js similarity index 100% rename from jstest/spec/nsa_spec.js rename to jstest/staging/nsa_spec.js diff --git a/jstest/spec/router_spec.js b/jstest/staging/router_spec.js similarity index 100% rename from jstest/spec/router_spec.js rename to jstest/staging/router_spec.js From 114dbf4d0d578c7d91bbe7ed33fa91f17474c54e Mon Sep 17 00:00:00 2001 From: David Inga Date: Mon, 30 Jun 2014 18:45:05 +0200 Subject: [PATCH 200/823] front end dependencies --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 68753d96e7..4382858195 100644 --- a/README.md +++ b/README.md @@ -58,6 +58,7 @@ Installing front end dependencies: ```bash $ npm install -g grunt-cli bower phantomjs +$ npm install -d && bower install ``` Almost there! Final steps are to update your `.env` file: From 38056748736e8fdb984c7588d82abfaa7f7909a8 Mon Sep 17 00:00:00 2001 From: David Inga Date: Mon, 30 Jun 2014 18:48:26 +0200 Subject: [PATCH 201/823] spec helpers --- jstest/config.js | 4 +--- jstest/spec/AnalysisService_spec.js | 2 +- jstest/spec/MapLayerService_spec.js | 2 +- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/jstest/config.js b/jstest/config.js index e93dfa1642..d232f9ca87 100644 --- a/jstest/config.js +++ b/jstest/config.js @@ -1,7 +1,5 @@ require.config({ - baseUrl: '', - paths: { jquery: '../vendor/assets/bower_components/jquery/dist/jquery', underscore: '../vendor/assets/bower_components/underscore/underscore', @@ -37,7 +35,7 @@ require.config({ // itertools: '../vendor/assets/javascripts/itertools', // spec: 'spec', - helpers: 'helpers', + helpers: '../jstest/helpers', // jasmine: 'lib/jasmine', // jasmine_html: 'lib/jasmine-html', // jasmine_boot: 'lib/boot', diff --git a/jstest/spec/AnalysisService_spec.js b/jstest/spec/AnalysisService_spec.js index 9921df6ea8..f2ad317d34 100644 --- a/jstest/spec/AnalysisService_spec.js +++ b/jstest/spec/AnalysisService_spec.js @@ -2,7 +2,7 @@ define([ 'services/AnalysisService', 'mps', 'underscore', - // 'helpers/api_responses', + 'helpers/api_responses', ], function(analysis, mps, _) { var k_combinations = function(set, k) { diff --git a/jstest/spec/MapLayerService_spec.js b/jstest/spec/MapLayerService_spec.js index c4a088307f..6192a1c270 100644 --- a/jstest/spec/MapLayerService_spec.js +++ b/jstest/spec/MapLayerService_spec.js @@ -6,7 +6,7 @@ define([ 'nsa', 'mps', 'underscore', - // 'helpers/api_responses' + 'helpers/api_responses' ], function(service, nsa, mps, _) { describe("The MapLayerService", function() { From df5c35edde40976cafba456674601b7cbda169ed Mon Sep 17 00:00:00 2001 From: David Inga Date: Mon, 30 Jun 2014 18:56:33 +0200 Subject: [PATCH 202/823] removed testem --- testem.yml | 9 --------- 1 file changed, 9 deletions(-) delete mode 100644 testem.yml diff --git a/testem.yml b/testem.yml deleted file mode 100644 index f97e743041..0000000000 --- a/testem.yml +++ /dev/null @@ -1,9 +0,0 @@ -framework: jasmine2 -test_page: jstest/index.html -src_files: -- app/assets/javascripts/map/** -- vendor/assets/javascripts/** -- jstest/** -launch_in_dev: -- Chrome -- Firefox \ No newline at end of file From deff7c999d69198ecbfca3445b721f04638d5db5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A1n=20P=C3=A9rez?= Date: Tue, 1 Jul 2014 09:44:34 +0200 Subject: [PATCH 203/823] show treshold on countries --- .../images/country-icons-s2a34c6c91a.png | Bin 8531 -> 0 bytes app/assets/javascripts/countries.js | 3 +- .../javascripts/countries/views/header.js | 12 +- app/assets/stylesheets/countries.scss | 215 ++++++++++++++++++ app/assets/stylesheets/countries/show.scss | 6 +- app/views/countries/_header.html.erb | 6 + app/views/countries/show.html.erb | 1 - lib/assets/javascripts/gfw/helpers.js.erb | 8 +- .../javascripts/gfw/ui/umd_options.js.erb | 14 +- 9 files changed, 252 insertions(+), 13 deletions(-) delete mode 100644 app/assets/images/country-icons-s2a34c6c91a.png diff --git a/app/assets/images/country-icons-s2a34c6c91a.png b/app/assets/images/country-icons-s2a34c6c91a.png deleted file mode 100644 index 7c164a4774a8697268d0814ddf8c18445ef9141e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8531 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 diff --git a/app/assets/javascripts/countries.js b/app/assets/javascripts/countries.js index 2126300988..4c6c304d59 100644 --- a/app/assets/javascripts/countries.js +++ b/app/assets/javascripts/countries.js @@ -6,4 +6,5 @@ //= require gfw/ui/widget //= require gfw/ui/sourcewindow //= require gfw/ui/share -//= require_tree ./countries \ No newline at end of file +//= require_tree ./countries +//= require gfw/ui/umd_options \ No newline at end of file diff --git a/app/assets/javascripts/countries/views/header.js b/app/assets/javascripts/countries/views/header.js index ac5399770e..3877cd740a 100644 --- a/app/assets/javascripts/countries/views/header.js +++ b/app/assets/javascripts/countries/views/header.js @@ -162,7 +162,8 @@ gfw.ui.view.CountryHeader = cdb.core.View.extend({ events: { 'change #areaSelector': '_onSelectArea', - 'click .selector-remove': '_navigateCountry' + 'click .selector-remove': '_navigateCountry', + 'click .umd_options_control' : '_onClickUMDOptions' }, initialize: function(options) { @@ -233,6 +234,15 @@ gfw.ui.view.CountryHeader = cdb.core.View.extend({ } }); }, + _onClickUMDOptions: function() { + UmdOptions = new gfw.ui.view.UmdOptions({ target: '.country-sidenav'}); + var $target = $('.umdoptions_dialog'); + if ($target.is(':visible') ) { + UmdOptions.hide(); + } else { + UmdOptions._openUMDoptions(); + } + }, _onSelectArea: function() { var self = this, diff --git a/app/assets/stylesheets/countries.scss b/app/assets/stylesheets/countries.scss index 56420f851e..e9f34ea377 100644 --- a/app/assets/stylesheets/countries.scss +++ b/app/assets/stylesheets/countries.scss @@ -21,6 +21,7 @@ $font-medium: "fira_sans_otmedium", Georgia, sans-serif; // Icons @import "country-icons/*.png"; +@import "home-icons/*.png"; @include all-country-icons-sprites; // Countries @@ -136,3 +137,217 @@ $font-medium: "fira_sans_otmedium", Georgia, sans-serif; } } +.umdoptions_dialog { + + background: #fff; + border: 1px solid #666; + @include border-radius(3px); + @include box-shadow(0px 0px 3px #666); + display: none; + float: none; + font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; + font-size: 16px; + position: relative; + left: -444px; + top: -250px; + width: 400px; + min-height: 250px; + padding: 20px 20px 0px 20px; + z-index: 1000; + + &::after { + content: ' '; + background-color: white; + width: 15px; + height: 15px; + float: left; + position: relative; + top: -59px; + left: 413px; + -webkit-transform: rotate(45deg); + transform: rotate(45deg); + z-index: 0; + } + + .close { + display: block; + position: absolute; + top: 15px; right: 15px; + width: 6px; + height: 6px; + @include home-icons-sprite(infowindow_close); + } + h2 { + color: #a1ba42; + border-bottom-color: #a1ba42; + border-bottom: 2px solid #ccc; + margin: 0 0 20px; + padding: 0 0 20px; + display: block; + font-weight: bold; + font-size: 13px; + text-transform: uppercase; + } + p { + color: #666; + margin-bottom: 10px; + &.canopy { + font-size: 11px; + font-weight: bold; + position: relative; + width: 159px; + .source { + top: 1px; + right: 7px; + width: 10px; + padding: 0; + } + } + &.trees { + height: 60px; + } + &.slider_container{ + position: relative; + } + span { + height: 60px; + float: left; + width: 91px; + &.forest_icon { + float: right; + background: image-url('backgrounds/map_options_tree-2.png') no-repeat; + } + &.tree_icon { + margin-top: 20px; + width: 25px; + height: 41px; + background: image-url('backgrounds/map_options_tree-1.png') no-repeat; + } + } + .slider { + background: image-url('/assets/icons/point.png') repeat-x 1px center; + top: -13px; + position: relative; + width: 395px; + #canopy_slider { + width: 295px; + margin-left: 45px; + -webkit-appearance: none; + background: image-url('/assets/icons/point.png') repeat-x -2px center; + position: relative; + top: 23px; + cursor: pointer; + &:focus { + outline: 0; + } + &::-webkit-slider-thumb { + -webkit-appearance: none; + background-color: #666; + width: 14px; + height: 14px; + @include home-icons-sprite(timeline_handler); + cursor: col-resize; + border-radius: 3px; + z-index: 500; + position:relative; + } + } + } + .visible_range { + display: block; + position: absolute; + top: 14px; + background: #A2BC28; + height: 6px; + box-shadow: 1px 0 3px #cccccc; + right: 0; + width: 350px; + border-top-right-radius: 3px; + border-bottom-right-radius: 3px; + pointer-events:none; + } + a { + @include inline-block(); + width: 70px; + padding: 10px 0; + border: #666666; + @include border-radius(3px); + font-weight:bold; + + text-align: center; + font-size:11px; + text-transform:uppercase; + text-decoration:none; + + &.apply { + background:#A1BA42; + color: #fff; + border: 1px solid #839C26; + + &:hover { + background: darken(#A1BA42, 10%); + border-color:darken(#A1BA42, 10%); + } + } + + &.cancel { + background: #fff; + border: 1px solid #CCCCCC; + color: #666666; + + &:hover { background:#f1f1f1; } + + } + } + &:last-child { + float: right; + margin-bottom: 0; + display: none; + } + } + ul { + width: 390px; + position: relative; + height: 12px; + li { + list-style: none; + display: inline; + color: #A2BE00; + font-size: 10px; + position: absolute; + cursor: pointer; + z-index: 5; + &.p0, + &.p100 { + color: #666; + left: 0; + cursor: default; + } + &.p10 { + left: calc(12% - 5px); + } + &.p15 { + left: calc(12*2% - 5px); + } + &.p20 { + left: calc(12*3% - 5px); + } + &.p25 { + left: calc(12*4% - 5px); + } + &.p30 { + left: calc(12*5% - 5px); + } + &.p50 { + left: calc(12*6% - 5px); + } + &.p75 { + left: calc(12*7% - 5px); + } + &.p100 { + left: calc(12*8% - 5px); + } + } + } + +} \ No newline at end of file diff --git a/app/assets/stylesheets/countries/show.scss b/app/assets/stylesheets/countries/show.scss index 5c0cfb6a71..1c1ae778fe 100644 --- a/app/assets/stylesheets/countries/show.scss +++ b/app/assets/stylesheets/countries/show.scss @@ -315,8 +315,12 @@ top: 17px; right: 20px; height: 18px; width: 18px; + &.country-icons-settings_engine { + height: 15px; + width: 15px; + } } - + &:hover { background: #464254; } diff --git a/app/views/countries/_header.html.erb b/app/views/countries/_header.html.erb index 0da1f3a605..28156125c8 100644 --- a/app/views/countries/_header.html.erb +++ b/app/views/countries/_header.html.erb @@ -57,6 +57,12 @@
    +
  • + + Treshold + + +
  • diff --git a/app/views/countries/show.html.erb b/app/views/countries/show.html.erb index 662a3bd739..488540d9b3 100644 --- a/app/views/countries/show.html.erb +++ b/app/views/countries/show.html.erb @@ -108,7 +108,6 @@
    No data available
    - Forest Type:
    • Regenerated
    • Primary
    • diff --git a/lib/assets/javascripts/gfw/helpers.js.erb b/lib/assets/javascripts/gfw/helpers.js.erb index d6d3f61fd3..167ce43f43 100644 --- a/lib/assets/javascripts/gfw/helpers.js.erb +++ b/lib/assets/javascripts/gfw/helpers.js.erb @@ -339,9 +339,9 @@ function isLandsat(basemap) { } function updateHash(options) { - var zoom = map.getZoom(), - lat = map.getCenter().lat().toFixed(2), - lng = map.getCenter().lng().toFixed(2), + var zoom = (typeof map !== 'undefined') ? map.getZoom() : 3, + lat = (typeof map !== 'undefined') ? map.getCenter().lat().toFixed(2) : 0, + lng = (typeof map !== 'undefined') ? map.getCenter().lng().toFixed(2) : 0, baselayer = config.BASELAYER === null ? 'none' : config.BASELAYER, layers = (config.MAPOPTIONS.layers.length > 0) ? '/'+config.MAPOPTIONS.layers : '', analysis = (config.BASELAYER === null || config.ISO !== 'ALL') ? '' : config.MAPOPTIONS.analysis, @@ -351,7 +351,7 @@ function updateHash(options) { if (options && options.replace) { window.router.navigate(hash, { replace: true }); - } else { + } else if (window.router && window.router) { window.router.navigate(hash, { trigger: true }); } } diff --git a/lib/assets/javascripts/gfw/ui/umd_options.js.erb b/lib/assets/javascripts/gfw/ui/umd_options.js.erb index 9c46f90c24..6d04f2217c 100644 --- a/lib/assets/javascripts/gfw/ui/umd_options.js.erb +++ b/lib/assets/javascripts/gfw/ui/umd_options.js.erb @@ -30,13 +30,15 @@ gfw.ui.view.UmdOptions = cdb.core.View.extend({ _initViews: function() { this.umdoptions_dialog = new gfw.ui.view.umdoptionsDialog({}); - - $('#map').append(this.umdoptions_dialog.render()); + if (this.options && this.options.target) { + $(this.options.target).append(this.umdoptions_dialog.render()); + } else { + $('#map').append(this.umdoptions_dialog.render()); + } }, _openUMDoptions: function(e) { - e.preventDefault(); - + e && e.preventDefault(); this.umdoptions_dialog.show(); }, @@ -235,7 +237,9 @@ gfw.ui.view.umdoptionsDialog = gfw.ui.view.Widget.extend({ } config.canopy_choice = this.canopy; updateHash(); - GFW.app.updateBaseLayer(config.BASELAYER) + if (typeof GFW !== 'undefined' && GFW.app) { + GFW.app.updateBaseLayer(config.BASELAYER) + } }, show: function() { From 32d492fb6226fe91a34720bca2081a176e00edd7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A1n=20P=C3=A9rez?= Date: Tue, 1 Jul 2014 09:44:57 +0200 Subject: [PATCH 204/823] show treshold on countries --- app/assets/images/country-icons-sd08f01b577.png | Bin 0 -> 8871 bytes .../images/country-icons/settings_engine.png | Bin 0 -> 378 bytes 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 app/assets/images/country-icons-sd08f01b577.png create mode 100644 app/assets/images/country-icons/settings_engine.png diff --git a/app/assets/images/country-icons-sd08f01b577.png b/app/assets/images/country-icons-sd08f01b577.png new file mode 100644 index 0000000000000000000000000000000000000000..6dc6ad63d93310ecbf4707e613652bdc608a22dc GIT binary patch literal 8871 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 literal 0 HcmV?d00001 From b8eda94a64a4e2f82ff1eebf46f201f70f5f7250 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A1n=20P=C3=A9rez?= Date: Tue, 1 Jul 2014 12:48:10 +0200 Subject: [PATCH 205/823] functioning treshold on country page --- .../javascripts/countries/views/header.js | 71 ++++++++++--------- .../javascripts/gfw/ui/umd_options.js.erb | 2 +- 2 files changed, 40 insertions(+), 33 deletions(-) diff --git a/app/assets/javascripts/countries/views/header.js b/app/assets/javascripts/countries/views/header.js index 3877cd740a..7a78816e67 100644 --- a/app/assets/javascripts/countries/views/header.js +++ b/app/assets/javascripts/countries/views/header.js @@ -4,25 +4,24 @@ gfw.ui.model.layersOptions = Backbone.Model.extend({ initialize: function(options) { options = options || {}; - + var treshold = (config.canopy_choice) ? config.canopy_choice : 10; var layers = { 'forest2000': { - url: 'http://earthengine.google.org/static/hansen_2013/tree_alpha/%z/%x/%y.png', + url: 'http://earthengine.google.org/static/hansen_2013/gfw_tree_loss_year_' + treshold + '/%z/%x/%y.png', dataMaxZoom: 12, tileSize: [256, 256], _filterCanvasImage: function(imageData, w, h) { - var components = 4, - pixelPos; - - for(var i = 0 ; i < w; ++i) { - for(var j = 0; j < h; ++j) { - var pixelPos = (j * w + i) * components, - intensity = imageData[pixelPos + 3]; - - imageData[pixelPos] = 0; - imageData[pixelPos + 1] = intensity * 0.7; - imageData[pixelPos + 2] = 0; - imageData[pixelPos+ 3] = intensity * 0.7; + var components = 4; //rgba + var pixel_pos; + + for(var i=0; i < w; ++i) { + for(var j=0; j < h; ++j) { + var pixel_pos = (j*w + i) * components; + var intensity = imageData[pixel_pos+1]; + imageData[pixel_pos] = 151; + imageData[pixel_pos + 1] = 189; + imageData[pixel_pos + 2] = 61; + imageData[pixel_pos+ 3] = intensity*0.8; } } } @@ -103,20 +102,6 @@ gfw.ui.view.leafletCanvasLayer = Backbone.View.extend({ }, _filterCanvasImage: function(imageData, w, h) { - var components = 4, - pixelPos; - - for(var i = 0 ; i < w; ++i) { - for(var j = 0; j < h; ++j) { - var pixelPos = (j * w + i) * components, - intensity = imageData[pixelPos + 3]; - - imageData[pixelPos] = 0; - imageData[pixelPos + 1] = intensity * 0.7; - imageData[pixelPos + 2] = 255; - imageData[pixelPos+ 3] = intensity * 0.7; - } - } }, _drawImageCanvas: function(canvas) { @@ -163,7 +148,9 @@ gfw.ui.view.CountryHeader = cdb.core.View.extend({ events: { 'change #areaSelector': '_onSelectArea', 'click .selector-remove': '_navigateCountry', - 'click .umd_options_control' : '_onClickUMDOptions' + 'click .umd_options_control' : '_onClickUMDOptions', + 'click .umdoptions_dialog .slider': '_updateMapTreshold', + 'click .umdoptions_dialog ul li': '_updateMapTreshold' }, initialize: function(options) { @@ -234,9 +221,12 @@ gfw.ui.view.CountryHeader = cdb.core.View.extend({ } }); }, - _onClickUMDOptions: function() { - UmdOptions = new gfw.ui.view.UmdOptions({ target: '.country-sidenav'}); + + _onClickUMDOptions: function(e) { + e && e.preventDefault(); + var $target = $('.umdoptions_dialog'); + if ($target.length === 0) UmdOptions = new gfw.ui.view.UmdOptions({ target: '.country-sidenav'}); if ($target.is(':visible') ) { UmdOptions.hide(); } else { @@ -244,6 +234,23 @@ gfw.ui.view.CountryHeader = cdb.core.View.extend({ } }, + _updateMapTreshold: function(e) { + var path = location.pathname.split('/'); + var id = path[path.length -1]; + var self = this; + this.map.remove(); + if (isNaN(id)) { + this._initMap(function() { + self._displayCountry(); + }); + } else { + var area = this.country.get('areas').where({ id_1: Number(id) })[0]; + this._initMap(function() { + self._displayArea(area); + }); + } + }, + _onSelectArea: function() { var self = this, areaName = this.$areaSelector.val(), @@ -297,7 +304,7 @@ gfw.ui.view.CountryHeader = cdb.core.View.extend({ } }).getLayer().addTo(this.map); - callback(); + callback && callback(); }, _removeCartodblayer: function() { diff --git a/lib/assets/javascripts/gfw/ui/umd_options.js.erb b/lib/assets/javascripts/gfw/ui/umd_options.js.erb index 6d04f2217c..4ecae7d53e 100644 --- a/lib/assets/javascripts/gfw/ui/umd_options.js.erb +++ b/lib/assets/javascripts/gfw/ui/umd_options.js.erb @@ -236,8 +236,8 @@ gfw.ui.view.umdoptionsDialog = gfw.ui.view.Widget.extend({ config.BASELAYER = 'loss' + this.canopy; } config.canopy_choice = this.canopy; - updateHash(); if (typeof GFW !== 'undefined' && GFW.app) { + updateHash(); GFW.app.updateBaseLayer(config.BASELAYER) } }, From f1f8e18f33988955d8904e9d05f3d7c835fb9666 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A1n=20P=C3=A9rez?= Date: Tue, 1 Jul 2014 12:59:05 +0200 Subject: [PATCH 206/823] change treshold triggerer --- app/assets/javascripts/countries/views/header.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/assets/javascripts/countries/views/header.js b/app/assets/javascripts/countries/views/header.js index 7a78816e67..871d84ba65 100644 --- a/app/assets/javascripts/countries/views/header.js +++ b/app/assets/javascripts/countries/views/header.js @@ -149,7 +149,7 @@ gfw.ui.view.CountryHeader = cdb.core.View.extend({ 'change #areaSelector': '_onSelectArea', 'click .selector-remove': '_navigateCountry', 'click .umd_options_control' : '_onClickUMDOptions', - 'click .umdoptions_dialog .slider': '_updateMapTreshold', + 'click .umdoptions_dialog #canopy_slider': '_updateMapTreshold', 'click .umdoptions_dialog ul li': '_updateMapTreshold' }, From 8aaadb5ea86af0c0448540dcfa3f63c2e43e9460 Mon Sep 17 00:00:00 2001 From: Pedro Garcia Date: Tue, 1 Jul 2014 15:15:42 +0200 Subject: [PATCH 207/823] layer validator --- .../map/presenters/LayersNavPresenter.js | 32 +++- .../map/presenters/MapPresenter.js | 20 +-- .../map/validators/LayerValidator.js | 46 +++++ .../javascripts/map/views/layersNavView.js | 6 +- config/requirejs.yml | 157 +++++++++--------- 5 files changed, 169 insertions(+), 92 deletions(-) create mode 100644 app/assets/javascripts/map/validators/LayerValidator.js diff --git a/app/assets/javascripts/map/presenters/LayersNavPresenter.js b/app/assets/javascripts/map/presenters/LayersNavPresenter.js index 6b6cd7a346..9f6b40ff5e 100644 --- a/app/assets/javascripts/map/presenters/LayersNavPresenter.js +++ b/app/assets/javascripts/map/presenters/LayersNavPresenter.js @@ -27,7 +27,33 @@ define([ * Subscribe to application events. */ _subscribe: function() { - mps.subscribe('Map/toggle-layer', this.view._toggleSelected); + mps.subscribe('LayerNav/toggle-layer', this.view._toggleSelected); + mps.publish('place/register', [this]); + }, + + /** + * Retuns place parameters representing the state of the LayerNavView and + * layers. Called by PlaceService. + * + * @return {Object} Params representing the state of the LayerNavView and layers + */ + + getPlaceParams: function() { + var params = {}; + var baseLayers = _.where(this.layers, {category_slug: 'forest_clearing'}); + var subLayers = _.filter(this.layers, function(layer) { + return layer.category_slug !== 'forest_clearing'; + }); + + params.baselayers = _.map(baseLayers, function(layer) { + return layer.slug; + }); + + params.sublayers = _.map(subLayers, function(layer) { + return layer.id; + }); + + return params; }, /** @@ -36,8 +62,10 @@ define([ * @param {string} layerName */ toggleLayer: function(layerName) { - mps.publish('Map/toggle-layer', [layerName]); + mps.publish('LayerNav/toggle-layer', [layerName]); + mps.publish('Place/update', [{go: false}]); } + }); return LayersNavPresenter; diff --git a/app/assets/javascripts/map/presenters/MapPresenter.js b/app/assets/javascripts/map/presenters/MapPresenter.js index aa28fb167f..34c8e50609 100644 --- a/app/assets/javascripts/map/presenters/MapPresenter.js +++ b/app/assets/javascripts/map/presenters/MapPresenter.js @@ -73,21 +73,21 @@ define([ getPlaceParams: function() { var params = {}; var mapCenter = this.view.getCenter(); - var baseLayers = _.where(this.layers, {category_slug: 'forest_clearing'}); - var subLayers = _.filter(this.layers, function(layer) { - return layer.category_slug !== 'forest_clearing'; - }); + // var baseLayers = _.where(this.layers, {category_slug: 'forest_clearing'}); + // var subLayers = _.filter(this.layers, function(layer) { + // return layer.category_slug !== 'forest_clearing'; + // }); params.zoom = this.view.getZoom(); params.lat = mapCenter.lat; params.lng = mapCenter.lng; params.maptype = this.view.getMapTypeId(); - params.baselayers = _.map(baseLayers, function(layer) { - return layer.slug; - }); - params.sublayers = _.map(subLayers, function(layer) { - return layer.id; - }); + // params.baselayers = _.map(baseLayers, function(layer) { + // return layer.slug; + // }); + // params.sublayers = _.map(subLayers, function(layer) { + // return layer.id; + // }); return params; }, diff --git a/app/assets/javascripts/map/validators/LayerValidator.js b/app/assets/javascripts/map/validators/LayerValidator.js new file mode 100644 index 0000000000..819daf199c --- /dev/null +++ b/app/assets/javascripts/map/validators/LayerValidator.js @@ -0,0 +1,46 @@ +/** + * The LayerValidator class. + * + * @return LayerValidator instance. + */ +define([ + 'Class', + 'underscore', + 'mps' +], function(Class, _, mps) { + + 'use strict'; + + var LayerValidator = Class.extend({ + + init: function() { + this.layers = []; + this._subscribe(); + }, + + /** + * Subscribe to application events. + */ + _subscribe: function() { + mps.subscribe('Place/go', _.bind(function(place) { + this.layers = place.params.layers; + }, this)); + }, + + /** + * Validates layers array. + * + * @return {boolean) True/false if validation passed. + */ + validate: function(layersArr) { + var result = true; + + return result; + } + + }); + + var layerValidator = new LayerValidator(); + + return layerValidator; +}); diff --git a/app/assets/javascripts/map/views/layersNavView.js b/app/assets/javascripts/map/views/layersNavView.js index 23f953d943..8ca7680b13 100644 --- a/app/assets/javascripts/map/views/layersNavView.js +++ b/app/assets/javascripts/map/views/layersNavView.js @@ -7,8 +7,9 @@ define([ 'backbone', 'underscore', 'presenters/LayersNavPresenter', + 'validators/LayerValidator', 'text!templates/layersNav.html' -], function(Backbone, _, Presenter, tpl) { +], function(Backbone, _, Presenter, layerValidator, tpl) { 'use strict'; @@ -24,6 +25,7 @@ define([ initialize: function() { _.bindAll(this, '_toggleSelected'); this.presenter = new Presenter(this); + this.layers = {}; this.render(); }, @@ -52,7 +54,7 @@ define([ var layerName = $(event.currentTarget).parents('li') .data('layer'); - if (layerName) { + if (layerName && layerValidator.validate(this.layers)) { this.presenter.toggleLayer(layerName) } }, diff --git a/config/requirejs.yml b/config/requirejs.yml index 890772aede..ab54d23f98 100644 --- a/config/requirejs.yml +++ b/config/requirejs.yml @@ -1,78 +1,79 @@ -modules: - - name: 'map' - -paths: - underscore: "underscore/underscore" - backbone: "backbone/backbone" - jquery: "jquery/dist/jquery" - d3: "d3/d3" - cartodb: "cartodb.js/dist/cartodb" - moment: "moment/moment" - text: "requirejs-text/text" - mps: "minpubsub/minpubsub" - - main: "map/main" - jqueryui: "jquery-ui-1.10.4.custom.min" - backbonequeryparams: "backbone.queryparams" - gmap: "map/gmap" - uri: "uri" - backbone_cartodb: "backbone.cartodb" - cartodblayer: "cartodb.gmapsv3" - wax: "wax.g.min" - store: "store" - Class: "class" - - app: "map/app" - utils: "map/utils" - router: "map/router" - mediator: "map/mediator" - presenter: "map/presenter" - views: "map/views" - templates: "map/templates" - services: "map/services" - presenters: "map/presenters" - models: "map/models" - collections: "map/collections" - jasmine: "jasmine" - analysis: "map/analysis" - nsa: "map/nsa" - -shim: - # main: - # deps: - # - "router" - # - "mediator" - # - "analysis" - # exports: - # "main" - underscore: - exports: "_" - backbone: - deps: - - "jquery" - - "underscore" - exports: "Backbone" - backbone_cartodb: - deps: - - "underscore" - - "backbone" - exports: "backbone_cartodb" - cartodblayer: - deps: - - "gmap" - exports: "CartoDBLayer" - jqueryui: - deps: - - "jquery" - backbonequeryparams: - deps: - - "backbone" - - "underscore" - exports: "backbonequeryparams" - Class: - exports: "Class" - uri: - exports: "UriTemplate" - user: - deps: - - "Class" +modules: + - name: 'map' + +paths: + underscore: "underscore/underscore" + backbone: "backbone/backbone" + jquery: "jquery/dist/jquery" + d3: "d3/d3" + cartodb: "cartodb.js/dist/cartodb" + moment: "moment/moment" + text: "requirejs-text/text" + mps: "minpubsub/minpubsub" + + main: "map/main" + jqueryui: "jquery-ui-1.10.4.custom.min" + backbonequeryparams: "backbone.queryparams" + gmap: "map/gmap" + uri: "uri" + backbone_cartodb: "backbone.cartodb" + cartodblayer: "cartodb.gmapsv3" + wax: "wax.g.min" + store: "store" + Class: "class" + + app: "map/app" + utils: "map/utils" + router: "map/router" + mediator: "map/mediator" + presenter: "map/presenter" + views: "map/views" + templates: "map/templates" + services: "map/services" + presenters: "map/presenters" + validators: "map/validators" + models: "map/models" + collections: "map/collections" + jasmine: "jasmine" + analysis: "map/analysis" + nsa: "map/nsa" + +shim: + # main: + # deps: + # - "router" + # - "mediator" + # - "analysis" + # exports: + # "main" + underscore: + exports: "_" + backbone: + deps: + - "jquery" + - "underscore" + exports: "Backbone" + backbone_cartodb: + deps: + - "underscore" + - "backbone" + exports: "backbone_cartodb" + cartodblayer: + deps: + - "gmap" + exports: "CartoDBLayer" + jqueryui: + deps: + - "jquery" + backbonequeryparams: + deps: + - "backbone" + - "underscore" + exports: "backbonequeryparams" + Class: + exports: "Class" + uri: + exports: "UriTemplate" + user: + deps: + - "Class" From fa4d247b8daf519f322062be5b4dfe09c7723ce6 Mon Sep 17 00:00:00 2001 From: Pedro Garcia Date: Tue, 1 Jul 2014 16:33:09 +0200 Subject: [PATCH 208/823] toggle layer, forest2000 working --- .../map/presenters/LayersNavPresenter.js | 37 +++++----- .../map/presenters/MapPresenter.js | 4 ++ .../map/validators/LayerValidator.js | 14 ++-- app/assets/javascripts/map/views/MapView.js | 71 ++++++++++++++----- .../views/layers/{forest.js => Forest2000.js} | 20 +++--- .../map/views/layers/UMDLossLayerView.js | 2 +- .../javascripts/map/views/layersNavView.js | 2 +- 7 files changed, 98 insertions(+), 52 deletions(-) rename app/assets/javascripts/map/views/layers/{forest.js => Forest2000.js} (61%) diff --git a/app/assets/javascripts/map/presenters/LayersNavPresenter.js b/app/assets/javascripts/map/presenters/LayersNavPresenter.js index 9f6b40ff5e..45693dcaaf 100644 --- a/app/assets/javascripts/map/presenters/LayersNavPresenter.js +++ b/app/assets/javascripts/map/presenters/LayersNavPresenter.js @@ -6,8 +6,9 @@ define([ 'Class', 'underscore', - 'mps' -], function(Class, _, mps) { + 'mps', + 'services/MapLayerService' +], function(Class, _, mps, mapLayerService) { 'use strict'; @@ -20,6 +21,7 @@ define([ */ init: function(view) { this.view = view; + this.mapLayerService = mapLayerService; this._subscribe(); }, @@ -40,18 +42,19 @@ define([ getPlaceParams: function() { var params = {}; - var baseLayers = _.where(this.layers, {category_slug: 'forest_clearing'}); - var subLayers = _.filter(this.layers, function(layer) { - return layer.category_slug !== 'forest_clearing'; - }); - params.baselayers = _.map(baseLayers, function(layer) { - return layer.slug; - }); + // var baseLayers = _.where(this.layers, {category_slug: 'forest_clearing'}); + // var subLayers = _.filter(this.layers, function(layer) { + // return layer.category_slug !== 'forest_clearing'; + // }); - params.sublayers = _.map(subLayers, function(layer) { - return layer.id; - }); + // params.baselayers = _.map(baseLayers, function(layer) { + // return layer.slug; + // }); + + // params.sublayers = _.map(subLayers, function(layer) { + // return layer.id; + // }); return params; }, @@ -59,11 +62,13 @@ define([ /** * Publish a a Map/toggle-layer. * - * @param {string} layerName + * @param {string} layerSlug */ - toggleLayer: function(layerName) { - mps.publish('LayerNav/toggle-layer', [layerName]); - mps.publish('Place/update', [{go: false}]); + toggleLayer: function(layerSlug) { + this.mapLayerService.getLayers([{slug: layerSlug}], function(layers) { + mps.publish('LayerNav/toggle-layer', [layers[0]]); + mps.publish('Place/update', [{go: false}]); + }); } }); diff --git a/app/assets/javascripts/map/presenters/MapPresenter.js b/app/assets/javascripts/map/presenters/MapPresenter.js index 34c8e50609..f19a0a88fe 100644 --- a/app/assets/javascripts/map/presenters/MapPresenter.js +++ b/app/assets/javascripts/map/presenters/MapPresenter.js @@ -41,6 +41,10 @@ define([ } }, this)); + mps.subscribe('LayerNav/toggle-layer', _.bind(function(layer) { + this.view.toggleLayer(layer) + },this)); + mps.publish('Place/register', [this]); }, diff --git a/app/assets/javascripts/map/validators/LayerValidator.js b/app/assets/javascripts/map/validators/LayerValidator.js index 819daf199c..31bb15d898 100644 --- a/app/assets/javascripts/map/validators/LayerValidator.js +++ b/app/assets/javascripts/map/validators/LayerValidator.js @@ -13,8 +13,14 @@ define([ var LayerValidator = Class.extend({ + baselayersOpts: { + allowCombined: [ + ['loss', 'gain'] + ] + }, + init: function() { - this.layers = []; + this.activeLayers = []; this._subscribe(); }, @@ -23,7 +29,7 @@ define([ */ _subscribe: function() { mps.subscribe('Place/go', _.bind(function(place) { - this.layers = place.params.layers; + this.activeLayers = place.params.layers; }, this)); }, @@ -32,12 +38,10 @@ define([ * * @return {boolean) True/false if validation passed. */ - validate: function(layersArr) { + validate: function(layerName) { var result = true; - return result; } - }); var layerValidator = new LayerValidator(); diff --git a/app/assets/javascripts/map/views/MapView.js b/app/assets/javascripts/map/views/MapView.js index cf9f4b16fc..5d77344975 100644 --- a/app/assets/javascripts/map/views/MapView.js +++ b/app/assets/javascripts/map/views/MapView.js @@ -8,8 +8,9 @@ define([ 'underscore', 'presenters/MapPresenter', 'views/AnalysisButtonView', - 'views/layers/UMDLossLayerView' -], function(Backbone, _, Presenter, AnalysisButtonView, UMDLossLayerView) { + 'views/layers/UMDLossLayerView', + 'views/layers/Forest2000' +], function(Backbone, _, Presenter, AnalysisButtonView, UMDLossLayerView, Forest2000Layer) { 'use strict'; @@ -17,12 +18,17 @@ define([ el: '#map', + layersViews: { + umd_tree_loss_gain: UMDLossLayerView, + forest2000: Forest2000Layer + }, + /** * Constructs a new MapView and its presenter. */ initialize: function() { this.presenter = new Presenter(this); - this.layerViews = {}; + this.layerViewsInst = {}; }, /** @@ -79,15 +85,47 @@ define([ _.map(layers, this.addLayer, this); }, + /** + * Used by map presenter to toggle a layer. + * + * @param {object} layer The layer object + */ + toggleLayer: function(layer) { + if (this._isLayerRendered(layer.slug)) { + this.removeLayer(layer.slug); + } else { + this.addLayer(layer); + } + }, + + /** + * Used by MapPresenter to add a layer to the map. + * + * @param {Object} layer The layer object + */ + addLayer: function(layer) { + var layerView = null; + + if (!_.has(this.layerViewsInst, layer.slug)) { + var LayerView = this.layersViews[layer.slug]; + layerView = new LayerView(layer); + this.layerViewsInst[layer.slug] = layerView; + } else { + layerView = this.layerViewsInst[layer.slug] + } + + this.map.overlayMapTypes.insertAt(0, layerView); + }, + /** * Used by MapPresenter to remove a layer by name. * * @param {string} name The name of the layer to remove */ removeLayer: function(name) { - var overlays_length = this.map.overlayMapTypes.getLength(); - if (overlays_length > 0) { - for (var i = 0; i< overlays_length; i++) { + var overlaysLength = this.map.overlayMapTypes.getLength(); + if (overlaysLength > 0) { + for (var i = 0; i< overlaysLength; i++) { var layer = this.map.overlayMapTypes.getAt(i); if (layer && layer.name === name) { this.map.overlayMapTypes.removeAt(i); @@ -97,21 +135,20 @@ define([ }, /** - * Used by MapPresenter to add a layer to the map. + * Check if a layer is already rendered by name. * - * @param {Object} layer The layer object + * @param {string} name The layer name */ - addLayer: function(layer) { - var layerView = null; - - if (layer.slug === 'umd_tree_loss_gain') { - if (!_.has(this.layerViews, layer.slug)) { - layerView = new UMDLossLayerView(layer); - this.layerViews[layer.slug] = layerView; + _isLayerRendered: function(name) { + var overlaysLength = this.map.overlayMapTypes.getLength(); + if (overlaysLength > 0) { + for (var i = 0; i< overlaysLength; i++) { + var layer = this.map.overlayMapTypes.getAt(i); + if (layer && layer.name === name) { + return true + } } } - - this.map.overlayMapTypes.insertAt(0, layerView); }, /** diff --git a/app/assets/javascripts/map/views/layers/forest.js b/app/assets/javascripts/map/views/layers/Forest2000.js similarity index 61% rename from app/assets/javascripts/map/views/layers/forest.js rename to app/assets/javascripts/map/views/layers/Forest2000.js index 31b583cb1f..031f85f10b 100644 --- a/app/assets/javascripts/map/views/layers/forest.js +++ b/app/assets/javascripts/map/views/layers/Forest2000.js @@ -4,22 +4,18 @@ * @return ForestLayer class (extends CanvasLayer) */ define([ - 'backbone', - 'mps', - 'presenter', - 'views/layers/class/canvasLayer', -], function(Backbone, mps, presenter, CanvasLayer) { + 'views/layers/class/CanvasLayer' +], function(CanvasLayer) { 'use strict'; - var ForestLayer = CanvasLayer.extend({ + var Forest2000Layer = CanvasLayer.extend({ - initialize: function() { + init: function(layer) { + this._super(); this.dataMaxZoom = 12; - this.name = 'forest2000'; - this.url = - 'http://earthengine.google.org/static/hansen_2013/tree_alpha/%z/%x/%y.png'; - ForestLayer.__super__.initialize.apply(this); + this.name = layer.slug; + this.urlTemplate = 'http://earthengine.google.org/static/hansen_2013/tree_alpha{/z}{/x}{/y}.png'; }, filterCanvasImage: function(imgdata, w, h) { @@ -41,6 +37,6 @@ define([ } }); - return ForestLayer; + return Forest2000Layer; }); diff --git a/app/assets/javascripts/map/views/layers/UMDLossLayerView.js b/app/assets/javascripts/map/views/layers/UMDLossLayerView.js index 1b82e84029..651e0eae9c 100644 --- a/app/assets/javascripts/map/views/layers/UMDLossLayerView.js +++ b/app/assets/javascripts/map/views/layers/UMDLossLayerView.js @@ -17,7 +17,7 @@ define([ this._super(); this.layer = layer; this.dataMaxZoom = 12; - this.name = 'loss'; + this.name = layer.slug; this.urlTemplate = 'http://earthengine.google.org/static/hansen_2013/gfw_loss_year{/z}{/x}{/y}.png'; this.presenter = new Presenter(this); }, diff --git a/app/assets/javascripts/map/views/layersNavView.js b/app/assets/javascripts/map/views/layersNavView.js index 8ca7680b13..b3f203a997 100644 --- a/app/assets/javascripts/map/views/layersNavView.js +++ b/app/assets/javascripts/map/views/layersNavView.js @@ -54,7 +54,7 @@ define([ var layerName = $(event.currentTarget).parents('li') .data('layer'); - if (layerName && layerValidator.validate(this.layers)) { + if (layerName && layerValidator.validate(layerName)) { this.presenter.toggleLayer(layerName) } }, From 5c7f3cea0e13e6d47c9d5cbf2c16b947af8cf098 Mon Sep 17 00:00:00 2001 From: Pedro Garcia Date: Tue, 1 Jul 2014 17:29:35 +0200 Subject: [PATCH 209/823] urltemplate, string format --- .../map/views/layers/class/CanvasLayer.js | 19 +- .../bower_components/backbone/.bower.json | 28 + .../bower_components/backbone/.npmignore | 7 + vendor/assets/bower_components/backbone/CNAME | 2 + .../bower_components/backbone/CONTRIBUTING.md | 22 + .../assets/bower_components/backbone/LICENSE | 22 + .../bower_components/backbone/README.md | 29 + .../bower_components/backbone/backbone.js | 1608 + .../bower_components/backbone/bower.json | 9 + .../bower_components/backbone/component.json | 13 + .../bower_components/backbone/index.html | 4617 +++ .../assets/bower_components/backbone/index.js | 1 + .../bower_components/backbone/package.json | 28 + .../bower_components/cartodb.js/.bower.json | 14 + .../bower_components/cartodb.js/.gitmodules | 3 + .../bower_components/cartodb.js/Makefile | 87 + .../assets/bower_components/cartodb.js/NEWS | 425 + .../bower_components/cartodb.js/README.md | 101 + .../cartodb.js/dist/cartodb.core.js | 4 + .../dist/cartodb.core.uncompressed.js | 3098 ++ .../cartodb.js/dist/cartodb.css | 3878 ++ .../dist/cartodb.full.uncompressed.js | 33961 ++++++++++++++++ .../cartodb.js/dist/cartodb.ie.css | 1 + .../cartodb.js/dist/cartodb.js | 20 + .../cartodb.js/dist/cartodb.mod.torque.js | 6 + .../dist/cartodb.mod.torque.uncompressed.js | 11501 ++++++ .../cartodb.js/dist/cartodb.nojquery.js | 17 + .../cartodb.js/dist/cartodb.noleaflet.js | 16 + .../cartodb.js/dist/cartodb.uncompressed.js | 32686 +++++++++++++++ .../bower_components/cartodb.js/doc/API.md | 1117 + .../bower_components/cartodb.js/doc/Makefile | 12 + .../cartodb.js/doc/doc_template.html | 63 + .../cartodb.js/doc/metrics.md | 36 + .../cartodb.js/doc/quickstart.md | 61 + .../bower_components/cartodb.js/doc/style.css | 61 + .../cartodb.js/doc/vizjson_format.md | 213 + .../examples/TheHobbitLocations/css/reset.css | 32 + .../examples/TheHobbitLocations/css/style.css | 220 + .../fonts/roboto-regular-webfont.eot | Bin 0 -> 19501 bytes .../fonts/roboto-regular-webfont.svg | 247 + .../fonts/roboto-regular-webfont.ttf | Bin 0 -> 41508 bytes .../fonts/roboto-regular-webfont.woff | Bin 0 -> 23232 bytes .../TheHobbitLocations/img/leftBkg.png | Bin 0 -> 3476 bytes .../TheHobbitLocations/img/legendBullets.png | Bin 0 -> 1517 bytes .../TheHobbitLocations/img/overlay.png | Bin 0 -> 48111 bytes .../examples/TheHobbitLocations/img/title.png | Bin 0 -> 21404 bytes .../TheHobbitLocations/img/videoButton.png | Bin 0 -> 474 bytes .../examples/TheHobbitLocations/index.html | 60 + .../examples/TheHobbitLocations/js/app.js | 39 + .../cartodb.js/examples/bounds.html | 61 + .../cartodb.js/examples/css/OLstyle.css | 543 + .../examples/cursor_interaction.html | 53 + .../examples/custom_infowindow.html | 67 + .../cartodb.js/examples/demo.html | 61 + .../cartodb.js/examples/easy.html | 60 + .../examples/filters-template/data.json | 44 + .../examples/filters-template/index.html | 29 + .../examples/filters-template/main.css | 49 + .../examples/filters-template/main.js | 161 + .../cartodb.js/examples/gmaps.html | 61 + .../cartodb.js/examples/gmaps_multilayer.html | 91 + .../cartodb.js/examples/gmaps_vector.html | 59 + .../examples/infowindow-on-hover.html | 51 + .../examples/infowindow_with_graph.html | 96 + .../cartodb.js/examples/layer_selector.html | 110 + .../examples/layer_selector_overlay.html | 57 + .../cartodb.js/examples/leaflet.html | 57 + .../examples/leaflet_hover_features.html | 108 + .../examples/leaflet_multilayer.html | 91 + .../cartodb.js/examples/leaflet_namedmap.html | 63 + .../cartodb.js/examples/leaflet_vector.html | 57 + .../examples/leaflet_vector_hover.html | 74 + .../leaflet_vector_query_distance.html | 82 + .../cartodb.js/examples/legends/custom.html | 113 + .../examples/legends/img/airport-24@2x.png | Bin 0 -> 1205 bytes .../examples/legends/img/heart-24@2x.png | Bin 0 -> 1459 bytes .../examples/legends/img/tennis-24@2x.png | Bin 0 -> 1261 bytes .../examples/legends/img/town-hall-24@2x.png | Bin 0 -> 899 bytes .../examples/legends/img/zoo-24@2x.png | Bin 0 -> 1509 bytes .../cartodb.js/examples/legends/index.html | 211 + .../cartodb.js/examples/legends/stacked.html | 178 + .../cartodb.js/examples/modestmaps.html | 58 + .../cartodb.js/examples/new-infowindow.html | 74 + .../cartodb.js/examples/openlayers.html | 78 + .../cartodb.js/examples/sublayer.html | 61 + .../cartodb.js/examples/time_slider.html | 103 + .../examples/tutorials/tutorial-1.html | 48 + .../examples/tutorials/tutorial-2.html | 73 + .../tutorial-query_by_distance-template.html | 60 + .../tutorials/tutorial-query_by_distance.html | 133 + .../tutorial-toggle_map_view-template.html | 58 + .../tutorials/tutorial-toggle_map_view.html | 103 + .../cartodb.js/examples/vendor/OpenLayers.js | 1696 + .../bower_components/cartodb.js/index.js | 92 + .../bower_components/cartodb.js/package.json | 31 + .../cartodb.js/scripts/cdn_invalidation.rb | 48 + .../cartodb.js/scripts/check_size.js | 24 + .../cartodb.js/scripts/compress.js | 104 + .../cartodb.js/scripts/core_footer.js | 3 + .../cartodb.js/scripts/core_header.js | 4 + .../cartodb.js/scripts/get.js | 17 + .../cartodb.js/scripts/mod.torque.footer.js | 5 + .../cartodb.js/scripts/print_lzma.js | 21 + .../cartodb.js/scripts/publish.js | 218 + .../cartodb.js/scripts/release.js | 36 + .../cartodb.js/scripts/wrapper.js | 63 + .../cartodb.js/src/api/core_lib.js | 152 + .../cartodb.js/src/api/layers.js | 199 + .../cartodb.js/src/api/sql.js | 306 + .../cartodb.js/src/api/tiles.js | 49 + .../cartodb.js/src/api/vis.js | 35 + .../cartodb.js/src/cartodb.js | 148 + .../cartodb.js/src/core/config.js | 38 + .../cartodb.js/src/core/decorator.js | 95 + .../cartodb.js/src/core/log.js | 84 + .../cartodb.js/src/core/model.js | 90 + .../cartodb.js/src/core/profiler.js | 165 + .../cartodb.js/src/core/template.js | 125 + .../cartodb.js/src/core/view.js | 171 + .../cartodb.js/src/geo/common.js | 266 + .../cartodb.js/src/geo/geocoder.js | 124 + .../cartodb.js/src/geo/geometry.js | 29 + .../src/geo/gmaps/gmaps.geometry.js | 258 + .../cartodb.js/src/geo/gmaps/gmaps.js | 327 + .../cartodb.js/src/geo/gmaps/gmaps_base.js | 79 + .../src/geo/gmaps/gmaps_baselayer.js | 41 + .../src/geo/gmaps/gmaps_cartodb_layer.js | 174 + .../src/geo/gmaps/gmaps_cartodb_layergroup.js | 451 + .../src/geo/gmaps/gmaps_plainlayer.js | 38 + .../src/geo/gmaps/gmaps_tiledlayer.js | 48 + .../cartodb.js/src/geo/gmaps/torque.js | 91 + .../cartodb.js/src/geo/layer_definition.js | 1139 + .../src/geo/leaflet/leaflet.geometry.js | 157 + .../cartodb.js/src/geo/leaflet/leaflet.js | 358 + .../src/geo/leaflet/leaflet_base.js | 51 + .../src/geo/leaflet/leaflet_cartodb_layer.js | 167 + .../geo/leaflet/leaflet_cartodb_layergroup.js | 433 + .../src/geo/leaflet/leaflet_plainlayer.js | 46 + .../src/geo/leaflet/leaflet_tiledlayer.js | 34 + .../src/geo/leaflet/leaflet_wmslayer.js | 38 + .../cartodb.js/src/geo/leaflet/torque.js | 92 + .../cartodb.js/src/geo/map.js | 756 + .../cartodb.js/src/geo/ui/fullscreen.js | 101 + .../cartodb.js/src/geo/ui/header.js | 12 + .../cartodb.js/src/geo/ui/infobox.js | 72 + .../cartodb.js/src/geo/ui/infowindow.js | 856 + .../cartodb.js/src/geo/ui/layer_selector.js | 237 + .../cartodb.js/src/geo/ui/legend.js | 1367 + .../cartodb.js/src/geo/ui/mobile.js | 162 + .../cartodb.js/src/geo/ui/search.js | 79 + .../cartodb.js/src/geo/ui/switcher.js | 115 + .../cartodb.js/src/geo/ui/tiles_loader.js | 56 + .../cartodb.js/src/geo/ui/time_slider.js | 171 + .../cartodb.js/src/geo/ui/tooltip.js | 174 + .../cartodb.js/src/geo/ui/zoom.js | 63 + .../cartodb.js/src/geo/ui/zoom_info.js | 24 + .../bower_components/cartodb.js/src/loader.js | 39 + .../cartodb.js/src/tests/cartofante_green.png | Bin 0 -> 481 bytes .../cartodb.js/src/tests/cartofante_grey.png | Bin 0 -> 480 bytes .../src/tests/cartofante_orange.png | Bin 0 -> 457 bytes .../cartodb.js/src/tests/cartofante_red.png | Bin 0 -> 481 bytes .../cartodb.js/src/ui/common/dialog.js | 166 + .../cartodb.js/src/ui/common/dropdown.js | 144 + .../cartodb.js/src/ui/common/notification.js | 81 + .../cartodb.js/src/ui/common/share.js | 183 + .../cartodb.js/src/ui/common/table.js | 448 + .../cartodb.js/src/ui/common/tabpane.js | 165 + .../cartodb.js/src/vis/layers.js | 118 + .../cartodb.js/src/vis/overlays.js | 359 + .../cartodb.js/src/vis/vis.js | 1110 + .../cartodb.js/test/SpecRunner.html | 214 + .../test/demos/geo/google_maps.html | 47 + .../cartodb.js/test/demos/geo/infowindow.tmpl | 103 + .../cartodb.js/test/demos/geo/map.html | 115 + .../cartodb.js/test/demos/index.html | 0 .../cartodb.js/test/demos/profiler.html | 18 + .../cartodb.js/test/demos/script.html | 12 + .../cartodb.js/test/demos/scroll_map.html | 101 + .../cartodb.js/test/demos/template.html | 19 + .../test/demos/ui/common/dialog.html | 76 + .../test/demos/ui/common/notification.html | 33 + .../test/demos/ui/common/settings.html | 74 + .../test/demos/ui/common/table.html | 55 + .../cartodb.js/test/demos/vis.html | 69 + .../test/lib/jasmine-1.2.0/MIT.LICENSE | 20 + .../test/lib/jasmine-1.2.0/jasmine-html.js | 616 + .../test/lib/jasmine-1.2.0/jasmine.css | 81 + .../test/lib/jasmine-1.2.0/jasmine.js | 2529 ++ .../test/lib/jasmine.junit_reporter.js | 200 + .../cartodb.js/test/lib/sinon-1.3.4.js | 3555 ++ .../cartodb.js/test/lib/sinon-ie.js | 82 + .../cartodb.js/test/phantomjs-testrunner.js | 187 + .../cartodb.js/test/runTests.sh | 2 + .../cartodb.js/test/spec/SpecHelper.js | 0 .../cartodb.js/test/spec/api/layers.spec.js | 414 + .../test/spec/api/layers/cartodb.spec.js | 185 + .../cartodb.js/test/spec/api/sql.spec.js | 226 + .../cartodb.js/test/spec/core/config.spec.js | 7 + .../test/spec/core/decorators.spec.js | 73 + .../cartodb.js/test/spec/core/log.spec.js | 37 + .../cartodb.js/test/spec/core/model.spec.js | 163 + .../test/spec/core/template.spec.js | 50 + .../cartodb.js/test/spec/core/view.spec.js | 162 + .../cartodb.js/test/spec/geo/common.spec.js | 49 + .../cartodb.js/test/spec/geo/geocoder.spec.js | 79 + .../cartodb.js/test/spec/geo/geometry.spec.js | 15 + .../cartodb.js/test/spec/geo/gmaps.spec.js | 256 + .../test/spec/geo/gmaps_cartodb_layer/hide.js | 68 + .../geo/gmaps_cartodb_layer/interaction.js | 49 + .../spec/geo/gmaps_cartodb_layer/opacity.js | 94 + .../test/spec/geo/gmaps_cartodb_layer/show.js | 68 + .../test/spec/geo/infowindow.spec.js | 447 + .../test/spec/geo/layer_definition.spec.js | 831 + .../test/spec/geo/layer_selector.spec.js | 155 + .../cartodb.js/test/spec/geo/leaflet.spec.js | 308 + .../cartodb.js/test/spec/geo/legend.spec.js | 928 + .../cartodb.js/test/spec/geo/map.spec.js | 146 + .../cartodb.js/test/spec/geo/ui/infobox.js | 45 + .../test/spec/geo/ui/tooltip.spec.js | 94 + .../test/spec/ui/common/dialog.spec.js | 57 + .../test/spec/ui/common/notification.spec.js | 40 + .../test/spec/ui/common/table.spec.js | 278 + .../test/spec/ui/common/tabpane.spec.js | 286 + .../cartodb.js/test/spec/vis/layers.spec.js | 34 + .../cartodb.js/test/spec/vis/vis.spec.js | 290 + .../cartodb.js/themes/css/cartodb.css | 3878 ++ .../cartodb.js/themes/css/ie/dummy.css | 1 + .../themes/css/images/layers-2x.png | Bin 0 -> 2898 bytes .../cartodb.js/themes/css/images/layers.png | Bin 0 -> 1502 bytes .../themes/css/images/marker-icon-2x.png | Bin 0 -> 4033 bytes .../themes/css/images/marker-icon.png | Bin 0 -> 1747 bytes .../themes/css/images/marker-shadow.png | Bin 0 -> 797 bytes .../cartodb.js/themes/css/images/zoom-in.png | Bin 0 -> 963 bytes .../cartodb.js/themes/css/images/zoom-out.png | Bin 0 -> 959 bytes .../infowindow/cartodb-infowindow-dark.css | 108 + .../infowindow/cartodb-infowindow-default.css | 474 + .../cartodb-infowindow-header-blue.css | 44 + .../cartodb-infowindow-header-default.css | 200 + .../cartodb-infowindow-header-green.css | 52 + .../cartodb-infowindow-header-orange.css | 52 + .../cartodb-infowindow-header-with-image.css | 165 + .../cartodb-infowindow-header-yellow.css | 51 + .../infowindow/cartodb-infowindow-light.css | 24 + .../themes/css/map/cartodb-map-light.css | 1817 + .../themes/css/map/cartodb-slider.css | 328 + .../cartodb.js/themes/css/map/leaflet.css | 478 + .../css/tooltip/cartodb-tooltip-dark.css | 23 + .../css/tooltip/cartodb-tooltip-default.css | 64 + .../css/tooltip/cartodb-tooltip-light.css | 6 + .../cartodb.js/themes/img/bubbles.png | Bin 0 -> 1038 bytes .../cartodb.js/themes/img/burguer.png | Bin 0 -> 2859 bytes .../cartodb.js/themes/img/burguer@2x.png | Bin 0 -> 2844 bytes .../cartodb.js/themes/img/dark.png | Bin 0 -> 1438 bytes .../cartodb.js/themes/img/facebook.png | Bin 0 -> 3017 bytes .../cartodb.js/themes/img/fullscreen.png | Bin 0 -> 2948 bytes .../cartodb.js/themes/img/handle.png | Bin 0 -> 2843 bytes .../cartodb.js/themes/img/handle@2x.png | Bin 0 -> 2910 bytes .../cartodb.js/themes/img/headers.png | Bin 0 -> 11915 bytes .../cartodb.js/themes/img/image_not_found.png | Bin 0 -> 54374 bytes .../cartodb.js/themes/img/light.png | Bin 0 -> 1704 bytes .../cartodb.js/themes/img/link.png | Bin 0 -> 3071 bytes .../cartodb.js/themes/img/loader.gif | Bin 0 -> 673 bytes .../cartodb.js/themes/img/loader@2x.gif | Bin 0 -> 2512 bytes .../cartodb.js/themes/img/other.png | Bin 0 -> 1677 bytes .../cartodb.js/themes/img/other@2x.png | Bin 0 -> 6785 bytes .../cartodb.js/themes/img/shadow.png | Bin 0 -> 2944 bytes .../cartodb.js/themes/img/share.png | Bin 0 -> 3114 bytes .../cartodb.js/themes/img/share@2x.png | Bin 0 -> 3148 bytes .../cartodb.js/themes/img/slider.png | Bin 0 -> 726 bytes .../cartodb.js/themes/img/twitter.png | Bin 0 -> 3012 bytes .../cartodb.js/vendor/GeoJSON.js | 229 + .../cartodb.js/vendor/backbone.js | 1431 + .../cartodb.js/vendor/jquery.faviconNotify.js | 209 + .../cartodb.js/vendor/jquery.min.js | 4 + .../cartodb.js/vendor/jscrollpane.js | 1435 + .../cartodb.js/vendor/json2.js | 486 + .../cartodb.js/vendor/leaflet.js | 9169 +++++ .../cartodb.js/vendor/lzma.js | 3882 ++ .../cartodb.js/vendor/mod/carto.js | 5252 +++ .../vendor/mod/jquery-ui/jquery.ui.core.js | 334 + .../vendor/mod/jquery-ui/jquery.ui.mouse.js | 167 + .../vendor/mod/jquery-ui/jquery.ui.slider.js | 674 + .../vendor/mod/jquery-ui/jquery.ui.widget.js | 272 + .../cartodb.js/vendor/mod/torque.cartodb.js | 1 + .../vendor/mod/torque.uncompressed.js | 4443 ++ .../cartodb.js/vendor/mousewheel.js | 84 + .../cartodb.js/vendor/mustache.js | 613 + .../cartodb.js/vendor/mwheelIntent.js | 76 + .../cartodb.js/vendor/reqwest.min.js | 514 + .../cartodb.js/vendor/spin.js | 2 + .../cartodb.js/vendor/underscore-min.js | 1 + .../cartodb.js/vendor/wax.cartodb.js | 3846 ++ vendor/assets/bower_components/d3/.bower.json | 34 + vendor/assets/bower_components/d3/.spmignore | 4 + .../bower_components/d3/CONTRIBUTING.md | 25 + vendor/assets/bower_components/d3/LICENSE | 26 + vendor/assets/bower_components/d3/README.md | 9 + vendor/assets/bower_components/d3/bower.json | 24 + .../assets/bower_components/d3/composer.json | 19 + vendor/assets/bower_components/d3/d3.js | 9243 +++++ vendor/assets/bower_components/d3/d3.min.js | 5 + .../bower_components/jquery/.bower.json | 37 + .../bower_components/jquery/MIT-LICENSE.txt | 21 + .../assets/bower_components/jquery/bower.json | 27 + .../bower_components/jquery/dist/jquery.js | 10308 +++++ .../jquery/dist/jquery.min.js | 5 + .../jquery/dist/jquery.min.map | 1 + .../bower_components/jquery/src/ajax.js | 807 + .../bower_components/jquery/src/ajax/jsonp.js | 89 + .../bower_components/jquery/src/ajax/load.js | 75 + .../jquery/src/ajax/parseJSON.js | 51 + .../jquery/src/ajax/parseXML.js | 31 + .../jquery/src/ajax/script.js | 93 + .../jquery/src/ajax/var/nonce.js | 5 + .../jquery/src/ajax/var/rquery.js | 3 + .../bower_components/jquery/src/ajax/xhr.js | 196 + .../bower_components/jquery/src/attributes.js | 11 + .../jquery/src/attributes/attr.js | 271 + .../jquery/src/attributes/classes.js | 157 + .../jquery/src/attributes/prop.js | 134 + .../jquery/src/attributes/support.js | 62 + .../jquery/src/attributes/val.js | 178 + .../bower_components/jquery/src/callbacks.js | 205 + .../bower_components/jquery/src/core.js | 534 + .../jquery/src/core/access.js | 60 + .../bower_components/jquery/src/core/init.js | 132 + .../jquery/src/core/parseHTML.js | 39 + .../bower_components/jquery/src/core/ready.js | 152 + .../jquery/src/core/var/rsingleTag.js | 4 + .../assets/bower_components/jquery/src/css.js | 504 + .../jquery/src/css/addGetHookIf.js | 32 + .../bower_components/jquery/src/css/curCSS.js | 117 + .../jquery/src/css/defaultDisplay.js | 69 + .../jquery/src/css/hiddenVisibleSelectors.js | 20 + .../jquery/src/css/support.js | 149 + .../bower_components/jquery/src/css/swap.js | 28 + .../jquery/src/css/var/cssExpand.js | 3 + .../jquery/src/css/var/isHidden.js | 13 + .../jquery/src/css/var/rmargin.js | 3 + .../jquery/src/css/var/rnumnonpx.js | 5 + .../bower_components/jquery/src/data.js | 335 + .../jquery/src/data/accepts.js | 21 + .../jquery/src/data/support.js | 25 + .../bower_components/jquery/src/deferred.js | 150 + .../bower_components/jquery/src/deprecated.js | 13 + .../bower_components/jquery/src/dimensions.js | 50 + .../bower_components/jquery/src/effects.js | 656 + .../jquery/src/effects/Tween.js | 114 + .../jquery/src/effects/animatedSelector.js | 13 + .../jquery/src/effects/support.js | 55 + .../bower_components/jquery/src/event.js | 1037 + .../jquery/src/event/alias.js | 39 + .../jquery/src/event/support.js | 26 + .../jquery/src/exports/amd.js | 24 + .../jquery/src/exports/global.js | 32 + .../bower_components/jquery/src/intro.js | 44 + .../bower_components/jquery/src/jquery.js | 37 + .../jquery/src/manipulation.js | 744 + .../jquery/src/manipulation/_evalUrl.js | 18 + .../jquery/src/manipulation/support.js | 76 + .../src/manipulation/var/rcheckableType.js | 3 + .../bower_components/jquery/src/offset.js | 211 + .../bower_components/jquery/src/outro.js | 1 + .../bower_components/jquery/src/queue.js | 142 + .../jquery/src/queue/delay.js | 22 + .../jquery/src/selector-sizzle.js | 14 + .../bower_components/jquery/src/selector.js | 1 + .../bower_components/jquery/src/serialize.js | 110 + .../jquery/src/sizzle/dist/sizzle.js | 2044 + .../jquery/src/sizzle/dist/sizzle.min.js | 3 + .../jquery/src/sizzle/dist/sizzle.min.map | 1 + .../bower_components/jquery/src/support.js | 58 + .../bower_components/jquery/src/traversing.js | 200 + .../jquery/src/traversing/findFilter.js | 100 + .../src/traversing/var/rneedsContext.js | 6 + .../jquery/src/var/class2type.js | 4 + .../bower_components/jquery/src/var/concat.js | 5 + .../jquery/src/var/deletedIds.js | 3 + .../bower_components/jquery/src/var/hasOwn.js | 5 + .../jquery/src/var/indexOf.js | 5 + .../bower_components/jquery/src/var/pnum.js | 3 + .../bower_components/jquery/src/var/push.js | 5 + .../jquery/src/var/rnotwhite.js | 3 + .../bower_components/jquery/src/var/slice.js | 5 + .../jquery/src/var/strundefined.js | 3 + .../jquery/src/var/support.js | 4 + .../jquery/src/var/toString.js | 5 + .../bower_components/jquery/src/wrap.js | 75 + .../bower_components/minpubsub/.bower.json | 28 + .../bower_components/minpubsub/History.md | 9 + .../bower_components/minpubsub/LICENSE.txt | 22 + .../bower_components/minpubsub/README.md | 130 + .../bower_components/minpubsub/bower.json | 18 + .../bower_components/minpubsub/minpubsub.js | 1 + .../minpubsub/minpubsub.src.js | 94 + .../bower_components/minpubsub/package.json | 21 + .../bower_components/minpubsub/unit-tests.htm | 121 + .../bower_components/moment/.bower.json | 30 + vendor/assets/bower_components/moment/LICENSE | 22 + .../assets/bower_components/moment/bower.json | 20 + .../bower_components/moment/lang/ar-ma.js | 56 + .../bower_components/moment/lang/ar-sa.js | 96 + .../assets/bower_components/moment/lang/ar.js | 97 + .../assets/bower_components/moment/lang/az.js | 102 + .../assets/bower_components/moment/lang/bg.js | 86 + .../assets/bower_components/moment/lang/bn.js | 106 + .../assets/bower_components/moment/lang/br.js | 107 + .../assets/bower_components/moment/lang/bs.js | 139 + .../assets/bower_components/moment/lang/ca.js | 66 + .../assets/bower_components/moment/lang/cs.js | 155 + .../assets/bower_components/moment/lang/cv.js | 59 + .../assets/bower_components/moment/lang/cy.js | 77 + .../assets/bower_components/moment/lang/da.js | 56 + .../bower_components/moment/lang/de-at.js | 72 + .../assets/bower_components/moment/lang/de.js | 71 + .../assets/bower_components/moment/lang/el.js | 90 + .../bower_components/moment/lang/en-au.js | 62 + .../bower_components/moment/lang/en-ca.js | 59 + .../bower_components/moment/lang/en-gb.js | 63 + .../assets/bower_components/moment/lang/eo.js | 65 + .../assets/bower_components/moment/lang/es.js | 75 + .../assets/bower_components/moment/lang/et.js | 76 + .../assets/bower_components/moment/lang/eu.js | 60 + .../assets/bower_components/moment/lang/fa.js | 97 + .../assets/bower_components/moment/lang/fi.js | 103 + .../assets/bower_components/moment/lang/fo.js | 56 + .../bower_components/moment/lang/fr-ca.js | 54 + .../assets/bower_components/moment/lang/fr.js | 58 + .../assets/bower_components/moment/lang/gl.js | 71 + .../assets/bower_components/moment/lang/he.js | 77 + .../assets/bower_components/moment/lang/hi.js | 105 + .../assets/bower_components/moment/lang/hr.js | 140 + .../assets/bower_components/moment/lang/hu.js | 105 + .../bower_components/moment/lang/hy-am.js | 113 + .../assets/bower_components/moment/lang/id.js | 67 + .../assets/bower_components/moment/lang/is.js | 124 + .../assets/bower_components/moment/lang/it.js | 59 + .../assets/bower_components/moment/lang/ja.js | 58 + .../assets/bower_components/moment/lang/ka.js | 108 + .../assets/bower_components/moment/lang/km.js | 55 + .../assets/bower_components/moment/lang/ko.js | 63 + .../assets/bower_components/moment/lang/lb.js | 160 + .../assets/bower_components/moment/lang/lt.js | 118 + .../assets/bower_components/moment/lang/lv.js | 77 + .../assets/bower_components/moment/lang/mk.js | 86 + .../assets/bower_components/moment/lang/ml.js | 64 + .../assets/bower_components/moment/lang/mr.js | 104 + .../bower_components/moment/lang/ms-my.js | 66 + .../assets/bower_components/moment/lang/nb.js | 57 + .../assets/bower_components/moment/lang/ne.js | 105 + .../assets/bower_components/moment/lang/nl.js | 67 + .../assets/bower_components/moment/lang/nn.js | 56 + .../assets/bower_components/moment/lang/pl.js | 98 + .../bower_components/moment/lang/pt-br.js | 56 + .../assets/bower_components/moment/lang/pt.js | 60 + .../assets/bower_components/moment/lang/ro.js | 72 + .../assets/bower_components/moment/lang/ru.js | 166 + .../assets/bower_components/moment/lang/sk.js | 156 + .../assets/bower_components/moment/lang/sl.js | 144 + .../assets/bower_components/moment/lang/sq.js | 61 + .../bower_components/moment/lang/sr-cyrl.js | 106 + .../assets/bower_components/moment/lang/sr.js | 106 + .../assets/bower_components/moment/lang/sv.js | 63 + .../assets/bower_components/moment/lang/ta.js | 112 + .../assets/bower_components/moment/lang/th.js | 58 + .../bower_components/moment/lang/tl-ph.js | 58 + .../assets/bower_components/moment/lang/tr.js | 93 + .../bower_components/moment/lang/tzm-latn.js | 55 + .../bower_components/moment/lang/tzm.js | 55 + .../assets/bower_components/moment/lang/uk.js | 157 + .../assets/bower_components/moment/lang/uz.js | 55 + .../assets/bower_components/moment/lang/vi.js | 62 + .../bower_components/moment/lang/zh-cn.js | 108 + .../bower_components/moment/lang/zh-tw.js | 84 + .../bower_components/moment/min/langs.js | 6426 +++ .../bower_components/moment/min/langs.min.js | 4 + .../moment/min/moment-with-langs.js | 8521 ++++ .../moment/min/moment-with-langs.min.js | 9 + .../bower_components/moment/min/moment.min.js | 6 + .../assets/bower_components/moment/moment.js | 2610 ++ .../assets/bower_components/moment/readme.md | 388 + .../requirejs-text/.bower.json | 14 + .../bower_components/requirejs-text/LICENSE | 58 + .../bower_components/requirejs-text/README.md | 198 + .../requirejs-text/package.json | 30 + .../bower_components/requirejs-text/text.js | 390 + .../bower_components/requirejs/.bower.json | 23 + .../bower_components/requirejs/README.md | 4 + .../bower_components/requirejs/bower.json | 14 + .../bower_components/requirejs/require.js | 2076 + .../bower_components/underscore/.bower.json | 33 + .../bower_components/underscore/.editorconfig | 14 + .../bower_components/underscore/LICENSE | 23 + .../bower_components/underscore/README.md | 22 + .../bower_components/underscore/bower.json | 8 + .../underscore/component.json | 10 + .../bower_components/underscore/package.json | 27 + .../bower_components/underscore/underscore.js | 1343 + 498 files changed, 228150 insertions(+), 9 deletions(-) create mode 100644 vendor/assets/bower_components/backbone/.bower.json create mode 100644 vendor/assets/bower_components/backbone/.npmignore create mode 100644 vendor/assets/bower_components/backbone/CNAME create mode 100644 vendor/assets/bower_components/backbone/CONTRIBUTING.md create mode 100644 vendor/assets/bower_components/backbone/LICENSE create mode 100644 vendor/assets/bower_components/backbone/README.md create mode 100644 vendor/assets/bower_components/backbone/backbone.js create mode 100644 vendor/assets/bower_components/backbone/bower.json create mode 100644 vendor/assets/bower_components/backbone/component.json create mode 100644 vendor/assets/bower_components/backbone/index.html create mode 100644 vendor/assets/bower_components/backbone/index.js create mode 100644 vendor/assets/bower_components/backbone/package.json create mode 100644 vendor/assets/bower_components/cartodb.js/.bower.json create mode 100644 vendor/assets/bower_components/cartodb.js/.gitmodules create mode 100644 vendor/assets/bower_components/cartodb.js/Makefile create mode 100644 vendor/assets/bower_components/cartodb.js/NEWS create mode 100644 vendor/assets/bower_components/cartodb.js/README.md create mode 100644 vendor/assets/bower_components/cartodb.js/dist/cartodb.core.js create mode 100644 vendor/assets/bower_components/cartodb.js/dist/cartodb.core.uncompressed.js create mode 100644 vendor/assets/bower_components/cartodb.js/dist/cartodb.css create mode 100644 vendor/assets/bower_components/cartodb.js/dist/cartodb.full.uncompressed.js create mode 100644 vendor/assets/bower_components/cartodb.js/dist/cartodb.ie.css create mode 100644 vendor/assets/bower_components/cartodb.js/dist/cartodb.js create mode 100644 vendor/assets/bower_components/cartodb.js/dist/cartodb.mod.torque.js create mode 100644 vendor/assets/bower_components/cartodb.js/dist/cartodb.mod.torque.uncompressed.js create mode 100644 vendor/assets/bower_components/cartodb.js/dist/cartodb.nojquery.js create mode 100644 vendor/assets/bower_components/cartodb.js/dist/cartodb.noleaflet.js create mode 100644 vendor/assets/bower_components/cartodb.js/dist/cartodb.uncompressed.js create mode 100644 vendor/assets/bower_components/cartodb.js/doc/API.md create mode 100644 vendor/assets/bower_components/cartodb.js/doc/Makefile create mode 100644 vendor/assets/bower_components/cartodb.js/doc/doc_template.html create mode 100644 vendor/assets/bower_components/cartodb.js/doc/metrics.md create mode 100644 vendor/assets/bower_components/cartodb.js/doc/quickstart.md create mode 100644 vendor/assets/bower_components/cartodb.js/doc/style.css create mode 100644 vendor/assets/bower_components/cartodb.js/doc/vizjson_format.md create mode 100644 vendor/assets/bower_components/cartodb.js/examples/TheHobbitLocations/css/reset.css create mode 100644 vendor/assets/bower_components/cartodb.js/examples/TheHobbitLocations/css/style.css create mode 100755 vendor/assets/bower_components/cartodb.js/examples/TheHobbitLocations/fonts/roboto-regular-webfont.eot create mode 100755 vendor/assets/bower_components/cartodb.js/examples/TheHobbitLocations/fonts/roboto-regular-webfont.svg create mode 100755 vendor/assets/bower_components/cartodb.js/examples/TheHobbitLocations/fonts/roboto-regular-webfont.ttf create mode 100755 vendor/assets/bower_components/cartodb.js/examples/TheHobbitLocations/fonts/roboto-regular-webfont.woff create mode 100644 vendor/assets/bower_components/cartodb.js/examples/TheHobbitLocations/img/leftBkg.png create mode 100644 vendor/assets/bower_components/cartodb.js/examples/TheHobbitLocations/img/legendBullets.png create mode 100644 vendor/assets/bower_components/cartodb.js/examples/TheHobbitLocations/img/overlay.png create mode 100644 vendor/assets/bower_components/cartodb.js/examples/TheHobbitLocations/img/title.png create mode 100644 vendor/assets/bower_components/cartodb.js/examples/TheHobbitLocations/img/videoButton.png create mode 100644 vendor/assets/bower_components/cartodb.js/examples/TheHobbitLocations/index.html create mode 100644 vendor/assets/bower_components/cartodb.js/examples/TheHobbitLocations/js/app.js create mode 100644 vendor/assets/bower_components/cartodb.js/examples/bounds.html create mode 100644 vendor/assets/bower_components/cartodb.js/examples/css/OLstyle.css create mode 100644 vendor/assets/bower_components/cartodb.js/examples/cursor_interaction.html create mode 100644 vendor/assets/bower_components/cartodb.js/examples/custom_infowindow.html create mode 100644 vendor/assets/bower_components/cartodb.js/examples/demo.html create mode 100644 vendor/assets/bower_components/cartodb.js/examples/easy.html create mode 100644 vendor/assets/bower_components/cartodb.js/examples/filters-template/data.json create mode 100644 vendor/assets/bower_components/cartodb.js/examples/filters-template/index.html create mode 100644 vendor/assets/bower_components/cartodb.js/examples/filters-template/main.css create mode 100644 vendor/assets/bower_components/cartodb.js/examples/filters-template/main.js create mode 100644 vendor/assets/bower_components/cartodb.js/examples/gmaps.html create mode 100644 vendor/assets/bower_components/cartodb.js/examples/gmaps_multilayer.html create mode 100644 vendor/assets/bower_components/cartodb.js/examples/gmaps_vector.html create mode 100644 vendor/assets/bower_components/cartodb.js/examples/infowindow-on-hover.html create mode 100644 vendor/assets/bower_components/cartodb.js/examples/infowindow_with_graph.html create mode 100644 vendor/assets/bower_components/cartodb.js/examples/layer_selector.html create mode 100644 vendor/assets/bower_components/cartodb.js/examples/layer_selector_overlay.html create mode 100644 vendor/assets/bower_components/cartodb.js/examples/leaflet.html create mode 100644 vendor/assets/bower_components/cartodb.js/examples/leaflet_hover_features.html create mode 100644 vendor/assets/bower_components/cartodb.js/examples/leaflet_multilayer.html create mode 100644 vendor/assets/bower_components/cartodb.js/examples/leaflet_namedmap.html create mode 100644 vendor/assets/bower_components/cartodb.js/examples/leaflet_vector.html create mode 100644 vendor/assets/bower_components/cartodb.js/examples/leaflet_vector_hover.html create mode 100644 vendor/assets/bower_components/cartodb.js/examples/leaflet_vector_query_distance.html create mode 100644 vendor/assets/bower_components/cartodb.js/examples/legends/custom.html create mode 100755 vendor/assets/bower_components/cartodb.js/examples/legends/img/airport-24@2x.png create mode 100755 vendor/assets/bower_components/cartodb.js/examples/legends/img/heart-24@2x.png create mode 100755 vendor/assets/bower_components/cartodb.js/examples/legends/img/tennis-24@2x.png create mode 100755 vendor/assets/bower_components/cartodb.js/examples/legends/img/town-hall-24@2x.png create mode 100755 vendor/assets/bower_components/cartodb.js/examples/legends/img/zoo-24@2x.png create mode 100644 vendor/assets/bower_components/cartodb.js/examples/legends/index.html create mode 100644 vendor/assets/bower_components/cartodb.js/examples/legends/stacked.html create mode 100644 vendor/assets/bower_components/cartodb.js/examples/modestmaps.html create mode 100644 vendor/assets/bower_components/cartodb.js/examples/new-infowindow.html create mode 100644 vendor/assets/bower_components/cartodb.js/examples/openlayers.html create mode 100644 vendor/assets/bower_components/cartodb.js/examples/sublayer.html create mode 100644 vendor/assets/bower_components/cartodb.js/examples/time_slider.html create mode 100644 vendor/assets/bower_components/cartodb.js/examples/tutorials/tutorial-1.html create mode 100644 vendor/assets/bower_components/cartodb.js/examples/tutorials/tutorial-2.html create mode 100644 vendor/assets/bower_components/cartodb.js/examples/tutorials/tutorial-query_by_distance-template.html create mode 100644 vendor/assets/bower_components/cartodb.js/examples/tutorials/tutorial-query_by_distance.html create mode 100644 vendor/assets/bower_components/cartodb.js/examples/tutorials/tutorial-toggle_map_view-template.html create mode 100644 vendor/assets/bower_components/cartodb.js/examples/tutorials/tutorial-toggle_map_view.html create mode 100644 vendor/assets/bower_components/cartodb.js/examples/vendor/OpenLayers.js create mode 100644 vendor/assets/bower_components/cartodb.js/index.js create mode 100644 vendor/assets/bower_components/cartodb.js/package.json create mode 100755 vendor/assets/bower_components/cartodb.js/scripts/cdn_invalidation.rb create mode 100644 vendor/assets/bower_components/cartodb.js/scripts/check_size.js create mode 100644 vendor/assets/bower_components/cartodb.js/scripts/compress.js create mode 100644 vendor/assets/bower_components/cartodb.js/scripts/core_footer.js create mode 100644 vendor/assets/bower_components/cartodb.js/scripts/core_header.js create mode 100644 vendor/assets/bower_components/cartodb.js/scripts/get.js create mode 100644 vendor/assets/bower_components/cartodb.js/scripts/mod.torque.footer.js create mode 100644 vendor/assets/bower_components/cartodb.js/scripts/print_lzma.js create mode 100644 vendor/assets/bower_components/cartodb.js/scripts/publish.js create mode 100644 vendor/assets/bower_components/cartodb.js/scripts/release.js create mode 100644 vendor/assets/bower_components/cartodb.js/scripts/wrapper.js create mode 100644 vendor/assets/bower_components/cartodb.js/src/api/core_lib.js create mode 100644 vendor/assets/bower_components/cartodb.js/src/api/layers.js create mode 100644 vendor/assets/bower_components/cartodb.js/src/api/sql.js create mode 100644 vendor/assets/bower_components/cartodb.js/src/api/tiles.js create mode 100644 vendor/assets/bower_components/cartodb.js/src/api/vis.js create mode 100644 vendor/assets/bower_components/cartodb.js/src/cartodb.js create mode 100644 vendor/assets/bower_components/cartodb.js/src/core/config.js create mode 100644 vendor/assets/bower_components/cartodb.js/src/core/decorator.js create mode 100644 vendor/assets/bower_components/cartodb.js/src/core/log.js create mode 100644 vendor/assets/bower_components/cartodb.js/src/core/model.js create mode 100644 vendor/assets/bower_components/cartodb.js/src/core/profiler.js create mode 100644 vendor/assets/bower_components/cartodb.js/src/core/template.js create mode 100644 vendor/assets/bower_components/cartodb.js/src/core/view.js create mode 100644 vendor/assets/bower_components/cartodb.js/src/geo/common.js create mode 100644 vendor/assets/bower_components/cartodb.js/src/geo/geocoder.js create mode 100644 vendor/assets/bower_components/cartodb.js/src/geo/geometry.js create mode 100644 vendor/assets/bower_components/cartodb.js/src/geo/gmaps/gmaps.geometry.js create mode 100644 vendor/assets/bower_components/cartodb.js/src/geo/gmaps/gmaps.js create mode 100644 vendor/assets/bower_components/cartodb.js/src/geo/gmaps/gmaps_base.js create mode 100644 vendor/assets/bower_components/cartodb.js/src/geo/gmaps/gmaps_baselayer.js create mode 100644 vendor/assets/bower_components/cartodb.js/src/geo/gmaps/gmaps_cartodb_layer.js create mode 100644 vendor/assets/bower_components/cartodb.js/src/geo/gmaps/gmaps_cartodb_layergroup.js create mode 100644 vendor/assets/bower_components/cartodb.js/src/geo/gmaps/gmaps_plainlayer.js create mode 100644 vendor/assets/bower_components/cartodb.js/src/geo/gmaps/gmaps_tiledlayer.js create mode 100644 vendor/assets/bower_components/cartodb.js/src/geo/gmaps/torque.js create mode 100644 vendor/assets/bower_components/cartodb.js/src/geo/layer_definition.js create mode 100644 vendor/assets/bower_components/cartodb.js/src/geo/leaflet/leaflet.geometry.js create mode 100644 vendor/assets/bower_components/cartodb.js/src/geo/leaflet/leaflet.js create mode 100644 vendor/assets/bower_components/cartodb.js/src/geo/leaflet/leaflet_base.js create mode 100755 vendor/assets/bower_components/cartodb.js/src/geo/leaflet/leaflet_cartodb_layer.js create mode 100644 vendor/assets/bower_components/cartodb.js/src/geo/leaflet/leaflet_cartodb_layergroup.js create mode 100644 vendor/assets/bower_components/cartodb.js/src/geo/leaflet/leaflet_plainlayer.js create mode 100644 vendor/assets/bower_components/cartodb.js/src/geo/leaflet/leaflet_tiledlayer.js create mode 100644 vendor/assets/bower_components/cartodb.js/src/geo/leaflet/leaflet_wmslayer.js create mode 100644 vendor/assets/bower_components/cartodb.js/src/geo/leaflet/torque.js create mode 100644 vendor/assets/bower_components/cartodb.js/src/geo/map.js create mode 100644 vendor/assets/bower_components/cartodb.js/src/geo/ui/fullscreen.js create mode 100644 vendor/assets/bower_components/cartodb.js/src/geo/ui/header.js create mode 100644 vendor/assets/bower_components/cartodb.js/src/geo/ui/infobox.js create mode 100644 vendor/assets/bower_components/cartodb.js/src/geo/ui/infowindow.js create mode 100644 vendor/assets/bower_components/cartodb.js/src/geo/ui/layer_selector.js create mode 100644 vendor/assets/bower_components/cartodb.js/src/geo/ui/legend.js create mode 100644 vendor/assets/bower_components/cartodb.js/src/geo/ui/mobile.js create mode 100644 vendor/assets/bower_components/cartodb.js/src/geo/ui/search.js create mode 100644 vendor/assets/bower_components/cartodb.js/src/geo/ui/switcher.js create mode 100644 vendor/assets/bower_components/cartodb.js/src/geo/ui/tiles_loader.js create mode 100644 vendor/assets/bower_components/cartodb.js/src/geo/ui/time_slider.js create mode 100644 vendor/assets/bower_components/cartodb.js/src/geo/ui/tooltip.js create mode 100644 vendor/assets/bower_components/cartodb.js/src/geo/ui/zoom.js create mode 100644 vendor/assets/bower_components/cartodb.js/src/geo/ui/zoom_info.js create mode 100644 vendor/assets/bower_components/cartodb.js/src/loader.js create mode 100644 vendor/assets/bower_components/cartodb.js/src/tests/cartofante_green.png create mode 100644 vendor/assets/bower_components/cartodb.js/src/tests/cartofante_grey.png create mode 100644 vendor/assets/bower_components/cartodb.js/src/tests/cartofante_orange.png create mode 100644 vendor/assets/bower_components/cartodb.js/src/tests/cartofante_red.png create mode 100644 vendor/assets/bower_components/cartodb.js/src/ui/common/dialog.js create mode 100644 vendor/assets/bower_components/cartodb.js/src/ui/common/dropdown.js create mode 100644 vendor/assets/bower_components/cartodb.js/src/ui/common/notification.js create mode 100644 vendor/assets/bower_components/cartodb.js/src/ui/common/share.js create mode 100644 vendor/assets/bower_components/cartodb.js/src/ui/common/table.js create mode 100644 vendor/assets/bower_components/cartodb.js/src/ui/common/tabpane.js create mode 100644 vendor/assets/bower_components/cartodb.js/src/vis/layers.js create mode 100644 vendor/assets/bower_components/cartodb.js/src/vis/overlays.js create mode 100644 vendor/assets/bower_components/cartodb.js/src/vis/vis.js create mode 100644 vendor/assets/bower_components/cartodb.js/test/SpecRunner.html create mode 100644 vendor/assets/bower_components/cartodb.js/test/demos/geo/google_maps.html create mode 100644 vendor/assets/bower_components/cartodb.js/test/demos/geo/infowindow.tmpl create mode 100644 vendor/assets/bower_components/cartodb.js/test/demos/geo/map.html create mode 100644 vendor/assets/bower_components/cartodb.js/test/demos/index.html create mode 100644 vendor/assets/bower_components/cartodb.js/test/demos/profiler.html create mode 100644 vendor/assets/bower_components/cartodb.js/test/demos/script.html create mode 100644 vendor/assets/bower_components/cartodb.js/test/demos/scroll_map.html create mode 100644 vendor/assets/bower_components/cartodb.js/test/demos/template.html create mode 100644 vendor/assets/bower_components/cartodb.js/test/demos/ui/common/dialog.html create mode 100644 vendor/assets/bower_components/cartodb.js/test/demos/ui/common/notification.html create mode 100644 vendor/assets/bower_components/cartodb.js/test/demos/ui/common/settings.html create mode 100644 vendor/assets/bower_components/cartodb.js/test/demos/ui/common/table.html create mode 100644 vendor/assets/bower_components/cartodb.js/test/demos/vis.html create mode 100644 vendor/assets/bower_components/cartodb.js/test/lib/jasmine-1.2.0/MIT.LICENSE create mode 100644 vendor/assets/bower_components/cartodb.js/test/lib/jasmine-1.2.0/jasmine-html.js create mode 100644 vendor/assets/bower_components/cartodb.js/test/lib/jasmine-1.2.0/jasmine.css create mode 100644 vendor/assets/bower_components/cartodb.js/test/lib/jasmine-1.2.0/jasmine.js create mode 100644 vendor/assets/bower_components/cartodb.js/test/lib/jasmine.junit_reporter.js create mode 100644 vendor/assets/bower_components/cartodb.js/test/lib/sinon-1.3.4.js create mode 100644 vendor/assets/bower_components/cartodb.js/test/lib/sinon-ie.js create mode 100644 vendor/assets/bower_components/cartodb.js/test/phantomjs-testrunner.js create mode 100755 vendor/assets/bower_components/cartodb.js/test/runTests.sh create mode 100644 vendor/assets/bower_components/cartodb.js/test/spec/SpecHelper.js create mode 100644 vendor/assets/bower_components/cartodb.js/test/spec/api/layers.spec.js create mode 100644 vendor/assets/bower_components/cartodb.js/test/spec/api/layers/cartodb.spec.js create mode 100644 vendor/assets/bower_components/cartodb.js/test/spec/api/sql.spec.js create mode 100644 vendor/assets/bower_components/cartodb.js/test/spec/core/config.spec.js create mode 100644 vendor/assets/bower_components/cartodb.js/test/spec/core/decorators.spec.js create mode 100644 vendor/assets/bower_components/cartodb.js/test/spec/core/log.spec.js create mode 100644 vendor/assets/bower_components/cartodb.js/test/spec/core/model.spec.js create mode 100644 vendor/assets/bower_components/cartodb.js/test/spec/core/template.spec.js create mode 100644 vendor/assets/bower_components/cartodb.js/test/spec/core/view.spec.js create mode 100644 vendor/assets/bower_components/cartodb.js/test/spec/geo/common.spec.js create mode 100644 vendor/assets/bower_components/cartodb.js/test/spec/geo/geocoder.spec.js create mode 100644 vendor/assets/bower_components/cartodb.js/test/spec/geo/geometry.spec.js create mode 100644 vendor/assets/bower_components/cartodb.js/test/spec/geo/gmaps.spec.js create mode 100644 vendor/assets/bower_components/cartodb.js/test/spec/geo/gmaps_cartodb_layer/hide.js create mode 100644 vendor/assets/bower_components/cartodb.js/test/spec/geo/gmaps_cartodb_layer/interaction.js create mode 100644 vendor/assets/bower_components/cartodb.js/test/spec/geo/gmaps_cartodb_layer/opacity.js create mode 100644 vendor/assets/bower_components/cartodb.js/test/spec/geo/gmaps_cartodb_layer/show.js create mode 100644 vendor/assets/bower_components/cartodb.js/test/spec/geo/infowindow.spec.js create mode 100644 vendor/assets/bower_components/cartodb.js/test/spec/geo/layer_definition.spec.js create mode 100644 vendor/assets/bower_components/cartodb.js/test/spec/geo/layer_selector.spec.js create mode 100644 vendor/assets/bower_components/cartodb.js/test/spec/geo/leaflet.spec.js create mode 100644 vendor/assets/bower_components/cartodb.js/test/spec/geo/legend.spec.js create mode 100644 vendor/assets/bower_components/cartodb.js/test/spec/geo/map.spec.js create mode 100644 vendor/assets/bower_components/cartodb.js/test/spec/geo/ui/infobox.js create mode 100644 vendor/assets/bower_components/cartodb.js/test/spec/geo/ui/tooltip.spec.js create mode 100644 vendor/assets/bower_components/cartodb.js/test/spec/ui/common/dialog.spec.js create mode 100644 vendor/assets/bower_components/cartodb.js/test/spec/ui/common/notification.spec.js create mode 100644 vendor/assets/bower_components/cartodb.js/test/spec/ui/common/table.spec.js create mode 100644 vendor/assets/bower_components/cartodb.js/test/spec/ui/common/tabpane.spec.js create mode 100644 vendor/assets/bower_components/cartodb.js/test/spec/vis/layers.spec.js create mode 100644 vendor/assets/bower_components/cartodb.js/test/spec/vis/vis.spec.js create mode 100644 vendor/assets/bower_components/cartodb.js/themes/css/cartodb.css create mode 100644 vendor/assets/bower_components/cartodb.js/themes/css/ie/dummy.css create mode 100755 vendor/assets/bower_components/cartodb.js/themes/css/images/layers-2x.png create mode 100755 vendor/assets/bower_components/cartodb.js/themes/css/images/layers.png create mode 100755 vendor/assets/bower_components/cartodb.js/themes/css/images/marker-icon-2x.png create mode 100755 vendor/assets/bower_components/cartodb.js/themes/css/images/marker-icon.png create mode 100755 vendor/assets/bower_components/cartodb.js/themes/css/images/marker-shadow.png create mode 100755 vendor/assets/bower_components/cartodb.js/themes/css/images/zoom-in.png create mode 100755 vendor/assets/bower_components/cartodb.js/themes/css/images/zoom-out.png create mode 100644 vendor/assets/bower_components/cartodb.js/themes/css/infowindow/cartodb-infowindow-dark.css create mode 100644 vendor/assets/bower_components/cartodb.js/themes/css/infowindow/cartodb-infowindow-default.css create mode 100644 vendor/assets/bower_components/cartodb.js/themes/css/infowindow/cartodb-infowindow-header-blue.css create mode 100644 vendor/assets/bower_components/cartodb.js/themes/css/infowindow/cartodb-infowindow-header-default.css create mode 100644 vendor/assets/bower_components/cartodb.js/themes/css/infowindow/cartodb-infowindow-header-green.css create mode 100644 vendor/assets/bower_components/cartodb.js/themes/css/infowindow/cartodb-infowindow-header-orange.css create mode 100644 vendor/assets/bower_components/cartodb.js/themes/css/infowindow/cartodb-infowindow-header-with-image.css create mode 100644 vendor/assets/bower_components/cartodb.js/themes/css/infowindow/cartodb-infowindow-header-yellow.css create mode 100644 vendor/assets/bower_components/cartodb.js/themes/css/infowindow/cartodb-infowindow-light.css create mode 100644 vendor/assets/bower_components/cartodb.js/themes/css/map/cartodb-map-light.css create mode 100755 vendor/assets/bower_components/cartodb.js/themes/css/map/cartodb-slider.css create mode 100755 vendor/assets/bower_components/cartodb.js/themes/css/map/leaflet.css create mode 100644 vendor/assets/bower_components/cartodb.js/themes/css/tooltip/cartodb-tooltip-dark.css create mode 100644 vendor/assets/bower_components/cartodb.js/themes/css/tooltip/cartodb-tooltip-default.css create mode 100644 vendor/assets/bower_components/cartodb.js/themes/css/tooltip/cartodb-tooltip-light.css create mode 100644 vendor/assets/bower_components/cartodb.js/themes/img/bubbles.png create mode 100644 vendor/assets/bower_components/cartodb.js/themes/img/burguer.png create mode 100644 vendor/assets/bower_components/cartodb.js/themes/img/burguer@2x.png create mode 100644 vendor/assets/bower_components/cartodb.js/themes/img/dark.png create mode 100644 vendor/assets/bower_components/cartodb.js/themes/img/facebook.png create mode 100644 vendor/assets/bower_components/cartodb.js/themes/img/fullscreen.png create mode 100644 vendor/assets/bower_components/cartodb.js/themes/img/handle.png create mode 100644 vendor/assets/bower_components/cartodb.js/themes/img/handle@2x.png create mode 100644 vendor/assets/bower_components/cartodb.js/themes/img/headers.png create mode 100644 vendor/assets/bower_components/cartodb.js/themes/img/image_not_found.png create mode 100644 vendor/assets/bower_components/cartodb.js/themes/img/light.png create mode 100644 vendor/assets/bower_components/cartodb.js/themes/img/link.png create mode 100644 vendor/assets/bower_components/cartodb.js/themes/img/loader.gif create mode 100644 vendor/assets/bower_components/cartodb.js/themes/img/loader@2x.gif create mode 100644 vendor/assets/bower_components/cartodb.js/themes/img/other.png create mode 100644 vendor/assets/bower_components/cartodb.js/themes/img/other@2x.png create mode 100644 vendor/assets/bower_components/cartodb.js/themes/img/shadow.png create mode 100644 vendor/assets/bower_components/cartodb.js/themes/img/share.png create mode 100644 vendor/assets/bower_components/cartodb.js/themes/img/share@2x.png create mode 100644 vendor/assets/bower_components/cartodb.js/themes/img/slider.png create mode 100644 vendor/assets/bower_components/cartodb.js/themes/img/twitter.png create mode 100644 vendor/assets/bower_components/cartodb.js/vendor/GeoJSON.js create mode 100644 vendor/assets/bower_components/cartodb.js/vendor/backbone.js create mode 100644 vendor/assets/bower_components/cartodb.js/vendor/jquery.faviconNotify.js create mode 100644 vendor/assets/bower_components/cartodb.js/vendor/jquery.min.js create mode 100644 vendor/assets/bower_components/cartodb.js/vendor/jscrollpane.js create mode 100644 vendor/assets/bower_components/cartodb.js/vendor/json2.js create mode 100644 vendor/assets/bower_components/cartodb.js/vendor/leaflet.js create mode 100644 vendor/assets/bower_components/cartodb.js/vendor/lzma.js create mode 100644 vendor/assets/bower_components/cartodb.js/vendor/mod/carto.js create mode 100644 vendor/assets/bower_components/cartodb.js/vendor/mod/jquery-ui/jquery.ui.core.js create mode 100644 vendor/assets/bower_components/cartodb.js/vendor/mod/jquery-ui/jquery.ui.mouse.js create mode 100644 vendor/assets/bower_components/cartodb.js/vendor/mod/jquery-ui/jquery.ui.slider.js create mode 100644 vendor/assets/bower_components/cartodb.js/vendor/mod/jquery-ui/jquery.ui.widget.js create mode 100644 vendor/assets/bower_components/cartodb.js/vendor/mod/torque.cartodb.js create mode 100644 vendor/assets/bower_components/cartodb.js/vendor/mod/torque.uncompressed.js create mode 100644 vendor/assets/bower_components/cartodb.js/vendor/mousewheel.js create mode 100644 vendor/assets/bower_components/cartodb.js/vendor/mustache.js create mode 100644 vendor/assets/bower_components/cartodb.js/vendor/mwheelIntent.js create mode 100644 vendor/assets/bower_components/cartodb.js/vendor/reqwest.min.js create mode 100644 vendor/assets/bower_components/cartodb.js/vendor/spin.js create mode 100644 vendor/assets/bower_components/cartodb.js/vendor/underscore-min.js create mode 100644 vendor/assets/bower_components/cartodb.js/vendor/wax.cartodb.js create mode 100644 vendor/assets/bower_components/d3/.bower.json create mode 100644 vendor/assets/bower_components/d3/.spmignore create mode 100644 vendor/assets/bower_components/d3/CONTRIBUTING.md create mode 100644 vendor/assets/bower_components/d3/LICENSE create mode 100644 vendor/assets/bower_components/d3/README.md create mode 100644 vendor/assets/bower_components/d3/bower.json create mode 100644 vendor/assets/bower_components/d3/composer.json create mode 100644 vendor/assets/bower_components/d3/d3.js create mode 100644 vendor/assets/bower_components/d3/d3.min.js create mode 100644 vendor/assets/bower_components/jquery/.bower.json create mode 100644 vendor/assets/bower_components/jquery/MIT-LICENSE.txt create mode 100644 vendor/assets/bower_components/jquery/bower.json create mode 100644 vendor/assets/bower_components/jquery/dist/jquery.js create mode 100644 vendor/assets/bower_components/jquery/dist/jquery.min.js create mode 100644 vendor/assets/bower_components/jquery/dist/jquery.min.map create mode 100644 vendor/assets/bower_components/jquery/src/ajax.js create mode 100644 vendor/assets/bower_components/jquery/src/ajax/jsonp.js create mode 100644 vendor/assets/bower_components/jquery/src/ajax/load.js create mode 100644 vendor/assets/bower_components/jquery/src/ajax/parseJSON.js create mode 100644 vendor/assets/bower_components/jquery/src/ajax/parseXML.js create mode 100644 vendor/assets/bower_components/jquery/src/ajax/script.js create mode 100644 vendor/assets/bower_components/jquery/src/ajax/var/nonce.js create mode 100644 vendor/assets/bower_components/jquery/src/ajax/var/rquery.js create mode 100644 vendor/assets/bower_components/jquery/src/ajax/xhr.js create mode 100644 vendor/assets/bower_components/jquery/src/attributes.js create mode 100644 vendor/assets/bower_components/jquery/src/attributes/attr.js create mode 100644 vendor/assets/bower_components/jquery/src/attributes/classes.js create mode 100644 vendor/assets/bower_components/jquery/src/attributes/prop.js create mode 100644 vendor/assets/bower_components/jquery/src/attributes/support.js create mode 100644 vendor/assets/bower_components/jquery/src/attributes/val.js create mode 100644 vendor/assets/bower_components/jquery/src/callbacks.js create mode 100644 vendor/assets/bower_components/jquery/src/core.js create mode 100644 vendor/assets/bower_components/jquery/src/core/access.js create mode 100644 vendor/assets/bower_components/jquery/src/core/init.js create mode 100644 vendor/assets/bower_components/jquery/src/core/parseHTML.js create mode 100644 vendor/assets/bower_components/jquery/src/core/ready.js create mode 100644 vendor/assets/bower_components/jquery/src/core/var/rsingleTag.js create mode 100644 vendor/assets/bower_components/jquery/src/css.js create mode 100644 vendor/assets/bower_components/jquery/src/css/addGetHookIf.js create mode 100644 vendor/assets/bower_components/jquery/src/css/curCSS.js create mode 100644 vendor/assets/bower_components/jquery/src/css/defaultDisplay.js create mode 100644 vendor/assets/bower_components/jquery/src/css/hiddenVisibleSelectors.js create mode 100644 vendor/assets/bower_components/jquery/src/css/support.js create mode 100644 vendor/assets/bower_components/jquery/src/css/swap.js create mode 100644 vendor/assets/bower_components/jquery/src/css/var/cssExpand.js create mode 100644 vendor/assets/bower_components/jquery/src/css/var/isHidden.js create mode 100644 vendor/assets/bower_components/jquery/src/css/var/rmargin.js create mode 100644 vendor/assets/bower_components/jquery/src/css/var/rnumnonpx.js create mode 100644 vendor/assets/bower_components/jquery/src/data.js create mode 100644 vendor/assets/bower_components/jquery/src/data/accepts.js create mode 100644 vendor/assets/bower_components/jquery/src/data/support.js create mode 100644 vendor/assets/bower_components/jquery/src/deferred.js create mode 100644 vendor/assets/bower_components/jquery/src/deprecated.js create mode 100644 vendor/assets/bower_components/jquery/src/dimensions.js create mode 100644 vendor/assets/bower_components/jquery/src/effects.js create mode 100644 vendor/assets/bower_components/jquery/src/effects/Tween.js create mode 100644 vendor/assets/bower_components/jquery/src/effects/animatedSelector.js create mode 100644 vendor/assets/bower_components/jquery/src/effects/support.js create mode 100644 vendor/assets/bower_components/jquery/src/event.js create mode 100644 vendor/assets/bower_components/jquery/src/event/alias.js create mode 100644 vendor/assets/bower_components/jquery/src/event/support.js create mode 100644 vendor/assets/bower_components/jquery/src/exports/amd.js create mode 100644 vendor/assets/bower_components/jquery/src/exports/global.js create mode 100644 vendor/assets/bower_components/jquery/src/intro.js create mode 100644 vendor/assets/bower_components/jquery/src/jquery.js create mode 100644 vendor/assets/bower_components/jquery/src/manipulation.js create mode 100644 vendor/assets/bower_components/jquery/src/manipulation/_evalUrl.js create mode 100644 vendor/assets/bower_components/jquery/src/manipulation/support.js create mode 100644 vendor/assets/bower_components/jquery/src/manipulation/var/rcheckableType.js create mode 100644 vendor/assets/bower_components/jquery/src/offset.js create mode 100644 vendor/assets/bower_components/jquery/src/outro.js create mode 100644 vendor/assets/bower_components/jquery/src/queue.js create mode 100644 vendor/assets/bower_components/jquery/src/queue/delay.js create mode 100644 vendor/assets/bower_components/jquery/src/selector-sizzle.js create mode 100644 vendor/assets/bower_components/jquery/src/selector.js create mode 100644 vendor/assets/bower_components/jquery/src/serialize.js create mode 100644 vendor/assets/bower_components/jquery/src/sizzle/dist/sizzle.js create mode 100644 vendor/assets/bower_components/jquery/src/sizzle/dist/sizzle.min.js create mode 100644 vendor/assets/bower_components/jquery/src/sizzle/dist/sizzle.min.map create mode 100644 vendor/assets/bower_components/jquery/src/support.js create mode 100644 vendor/assets/bower_components/jquery/src/traversing.js create mode 100644 vendor/assets/bower_components/jquery/src/traversing/findFilter.js create mode 100644 vendor/assets/bower_components/jquery/src/traversing/var/rneedsContext.js create mode 100644 vendor/assets/bower_components/jquery/src/var/class2type.js create mode 100644 vendor/assets/bower_components/jquery/src/var/concat.js create mode 100644 vendor/assets/bower_components/jquery/src/var/deletedIds.js create mode 100644 vendor/assets/bower_components/jquery/src/var/hasOwn.js create mode 100644 vendor/assets/bower_components/jquery/src/var/indexOf.js create mode 100644 vendor/assets/bower_components/jquery/src/var/pnum.js create mode 100644 vendor/assets/bower_components/jquery/src/var/push.js create mode 100644 vendor/assets/bower_components/jquery/src/var/rnotwhite.js create mode 100644 vendor/assets/bower_components/jquery/src/var/slice.js create mode 100644 vendor/assets/bower_components/jquery/src/var/strundefined.js create mode 100644 vendor/assets/bower_components/jquery/src/var/support.js create mode 100644 vendor/assets/bower_components/jquery/src/var/toString.js create mode 100644 vendor/assets/bower_components/jquery/src/wrap.js create mode 100644 vendor/assets/bower_components/minpubsub/.bower.json create mode 100644 vendor/assets/bower_components/minpubsub/History.md create mode 100644 vendor/assets/bower_components/minpubsub/LICENSE.txt create mode 100644 vendor/assets/bower_components/minpubsub/README.md create mode 100644 vendor/assets/bower_components/minpubsub/bower.json create mode 100644 vendor/assets/bower_components/minpubsub/minpubsub.js create mode 100644 vendor/assets/bower_components/minpubsub/minpubsub.src.js create mode 100644 vendor/assets/bower_components/minpubsub/package.json create mode 100644 vendor/assets/bower_components/minpubsub/unit-tests.htm create mode 100644 vendor/assets/bower_components/moment/.bower.json create mode 100644 vendor/assets/bower_components/moment/LICENSE create mode 100644 vendor/assets/bower_components/moment/bower.json create mode 100644 vendor/assets/bower_components/moment/lang/ar-ma.js create mode 100644 vendor/assets/bower_components/moment/lang/ar-sa.js create mode 100644 vendor/assets/bower_components/moment/lang/ar.js create mode 100644 vendor/assets/bower_components/moment/lang/az.js create mode 100644 vendor/assets/bower_components/moment/lang/bg.js create mode 100644 vendor/assets/bower_components/moment/lang/bn.js create mode 100644 vendor/assets/bower_components/moment/lang/br.js create mode 100644 vendor/assets/bower_components/moment/lang/bs.js create mode 100644 vendor/assets/bower_components/moment/lang/ca.js create mode 100644 vendor/assets/bower_components/moment/lang/cs.js create mode 100644 vendor/assets/bower_components/moment/lang/cv.js create mode 100644 vendor/assets/bower_components/moment/lang/cy.js create mode 100644 vendor/assets/bower_components/moment/lang/da.js create mode 100644 vendor/assets/bower_components/moment/lang/de-at.js create mode 100644 vendor/assets/bower_components/moment/lang/de.js create mode 100644 vendor/assets/bower_components/moment/lang/el.js create mode 100644 vendor/assets/bower_components/moment/lang/en-au.js create mode 100644 vendor/assets/bower_components/moment/lang/en-ca.js create mode 100644 vendor/assets/bower_components/moment/lang/en-gb.js create mode 100644 vendor/assets/bower_components/moment/lang/eo.js create mode 100644 vendor/assets/bower_components/moment/lang/es.js create mode 100644 vendor/assets/bower_components/moment/lang/et.js create mode 100644 vendor/assets/bower_components/moment/lang/eu.js create mode 100644 vendor/assets/bower_components/moment/lang/fa.js create mode 100644 vendor/assets/bower_components/moment/lang/fi.js create mode 100644 vendor/assets/bower_components/moment/lang/fo.js create mode 100644 vendor/assets/bower_components/moment/lang/fr-ca.js create mode 100644 vendor/assets/bower_components/moment/lang/fr.js create mode 100644 vendor/assets/bower_components/moment/lang/gl.js create mode 100644 vendor/assets/bower_components/moment/lang/he.js create mode 100644 vendor/assets/bower_components/moment/lang/hi.js create mode 100644 vendor/assets/bower_components/moment/lang/hr.js create mode 100644 vendor/assets/bower_components/moment/lang/hu.js create mode 100644 vendor/assets/bower_components/moment/lang/hy-am.js create mode 100644 vendor/assets/bower_components/moment/lang/id.js create mode 100644 vendor/assets/bower_components/moment/lang/is.js create mode 100644 vendor/assets/bower_components/moment/lang/it.js create mode 100644 vendor/assets/bower_components/moment/lang/ja.js create mode 100644 vendor/assets/bower_components/moment/lang/ka.js create mode 100644 vendor/assets/bower_components/moment/lang/km.js create mode 100644 vendor/assets/bower_components/moment/lang/ko.js create mode 100644 vendor/assets/bower_components/moment/lang/lb.js create mode 100644 vendor/assets/bower_components/moment/lang/lt.js create mode 100644 vendor/assets/bower_components/moment/lang/lv.js create mode 100644 vendor/assets/bower_components/moment/lang/mk.js create mode 100644 vendor/assets/bower_components/moment/lang/ml.js create mode 100644 vendor/assets/bower_components/moment/lang/mr.js create mode 100644 vendor/assets/bower_components/moment/lang/ms-my.js create mode 100644 vendor/assets/bower_components/moment/lang/nb.js create mode 100644 vendor/assets/bower_components/moment/lang/ne.js create mode 100644 vendor/assets/bower_components/moment/lang/nl.js create mode 100644 vendor/assets/bower_components/moment/lang/nn.js create mode 100644 vendor/assets/bower_components/moment/lang/pl.js create mode 100644 vendor/assets/bower_components/moment/lang/pt-br.js create mode 100644 vendor/assets/bower_components/moment/lang/pt.js create mode 100644 vendor/assets/bower_components/moment/lang/ro.js create mode 100644 vendor/assets/bower_components/moment/lang/ru.js create mode 100644 vendor/assets/bower_components/moment/lang/sk.js create mode 100644 vendor/assets/bower_components/moment/lang/sl.js create mode 100644 vendor/assets/bower_components/moment/lang/sq.js create mode 100644 vendor/assets/bower_components/moment/lang/sr-cyrl.js create mode 100644 vendor/assets/bower_components/moment/lang/sr.js create mode 100644 vendor/assets/bower_components/moment/lang/sv.js create mode 100644 vendor/assets/bower_components/moment/lang/ta.js create mode 100644 vendor/assets/bower_components/moment/lang/th.js create mode 100644 vendor/assets/bower_components/moment/lang/tl-ph.js create mode 100644 vendor/assets/bower_components/moment/lang/tr.js create mode 100644 vendor/assets/bower_components/moment/lang/tzm-latn.js create mode 100644 vendor/assets/bower_components/moment/lang/tzm.js create mode 100644 vendor/assets/bower_components/moment/lang/uk.js create mode 100644 vendor/assets/bower_components/moment/lang/uz.js create mode 100644 vendor/assets/bower_components/moment/lang/vi.js create mode 100644 vendor/assets/bower_components/moment/lang/zh-cn.js create mode 100644 vendor/assets/bower_components/moment/lang/zh-tw.js create mode 100644 vendor/assets/bower_components/moment/min/langs.js create mode 100644 vendor/assets/bower_components/moment/min/langs.min.js create mode 100644 vendor/assets/bower_components/moment/min/moment-with-langs.js create mode 100644 vendor/assets/bower_components/moment/min/moment-with-langs.min.js create mode 100644 vendor/assets/bower_components/moment/min/moment.min.js create mode 100644 vendor/assets/bower_components/moment/moment.js create mode 100644 vendor/assets/bower_components/moment/readme.md create mode 100644 vendor/assets/bower_components/requirejs-text/.bower.json create mode 100644 vendor/assets/bower_components/requirejs-text/LICENSE create mode 100644 vendor/assets/bower_components/requirejs-text/README.md create mode 100644 vendor/assets/bower_components/requirejs-text/package.json create mode 100644 vendor/assets/bower_components/requirejs-text/text.js create mode 100644 vendor/assets/bower_components/requirejs/.bower.json create mode 100644 vendor/assets/bower_components/requirejs/README.md create mode 100644 vendor/assets/bower_components/requirejs/bower.json create mode 100644 vendor/assets/bower_components/requirejs/require.js create mode 100644 vendor/assets/bower_components/underscore/.bower.json create mode 100644 vendor/assets/bower_components/underscore/.editorconfig create mode 100644 vendor/assets/bower_components/underscore/LICENSE create mode 100644 vendor/assets/bower_components/underscore/README.md create mode 100644 vendor/assets/bower_components/underscore/bower.json create mode 100644 vendor/assets/bower_components/underscore/component.json create mode 100644 vendor/assets/bower_components/underscore/package.json create mode 100644 vendor/assets/bower_components/underscore/underscore.js diff --git a/app/assets/javascripts/map/views/layers/class/CanvasLayer.js b/app/assets/javascripts/map/views/layers/class/CanvasLayer.js index f088725d0d..365b71430b 100644 --- a/app/assets/javascripts/map/views/layers/class/CanvasLayer.js +++ b/app/assets/javascripts/map/views/layers/class/CanvasLayer.js @@ -5,8 +5,9 @@ */ define([ 'Class', - 'underscore' -], function(Class, _) { + 'underscore', + 'uri' +], function(Class, _, UriTemplate) { 'use strict'; @@ -127,7 +128,7 @@ define([ }, _getUrl: function(x, y, z) { - return this.urlTemplate.replace('%z', z).replace('%x', x).replace('%y', y); + return new UriTemplate(this._urlTemplate).fillFromObject({x: x, y: y, z: z}); }, _getTileCoords: function(x, y, z) { @@ -159,7 +160,7 @@ define([ }, _getTileId: function(x, y, z) { - return x + '_' + y + '_' + z; + return '{0}_{1}_{2}'.format(x, y, z); }, updateTiles: function() { @@ -171,12 +172,12 @@ define([ /** * Filters the canvas imagedata. Subclasses implement this. * - * @param {object} imgdata - * @param {integer} w width - * @param {integer} h height - * @param {integer} zoom + * @param {object} imgdata Canvas tile image data + * @param {integer} w tile width + * @param {integer} h tile height + * @param {integer} z map zoom */ - filterCanvasImgdata: function(imgdata, w, h, zoom) { + filterCanvasImgdata: function(imgdata, w, h, z) { } }); diff --git a/vendor/assets/bower_components/backbone/.bower.json b/vendor/assets/bower_components/backbone/.bower.json new file mode 100644 index 0000000000..69fa15bb8e --- /dev/null +++ b/vendor/assets/bower_components/backbone/.bower.json @@ -0,0 +1,28 @@ +{ + "name": "backbone", + "version": "1.1.2", + "main": "backbone.js", + "dependencies": { + "underscore": ">=1.5.0" + }, + "ignore": [ + "backbone-min.js", + "docs", + "examples", + "test", + "*.yml", + "*.map", + ".html", + "*.ico" + ], + "homepage": "https://github.com/jashkenas/backbone", + "_release": "1.1.2", + "_resolution": { + "type": "version", + "tag": "1.1.2", + "commit": "53f77901a4ea9c7cf75d3db93ddddf491998d90f" + }, + "_source": "git://github.com/jashkenas/backbone.git", + "_target": "~1.1.2", + "_originalSource": "backbone" +} \ No newline at end of file diff --git a/vendor/assets/bower_components/backbone/.npmignore b/vendor/assets/bower_components/backbone/.npmignore new file mode 100644 index 0000000000..6bcb69f509 --- /dev/null +++ b/vendor/assets/bower_components/backbone/.npmignore @@ -0,0 +1,7 @@ +test/ +Rakefile +docs/ +raw/ +examples/ +index.html +.jshintrc diff --git a/vendor/assets/bower_components/backbone/CNAME b/vendor/assets/bower_components/backbone/CNAME new file mode 100644 index 0000000000..dfadb7985a --- /dev/null +++ b/vendor/assets/bower_components/backbone/CNAME @@ -0,0 +1,2 @@ +backbonejs.org + diff --git a/vendor/assets/bower_components/backbone/CONTRIBUTING.md b/vendor/assets/bower_components/backbone/CONTRIBUTING.md new file mode 100644 index 0000000000..55403616b9 --- /dev/null +++ b/vendor/assets/bower_components/backbone/CONTRIBUTING.md @@ -0,0 +1,22 @@ +## How to Open a Backbone.js Ticket + +* Do not use tickets to ask for help with (debugging) your application. Ask on +the [mailing list](https://groups.google.com/forum/#!forum/backbonejs), +in the IRC channel (`#documentcloud` on Freenode), or if you understand your +specific problem, on [StackOverflow](http://stackoverflow.com/questions/tagged/backbone.js). + +* Before you open a ticket or send a pull request, +[search](https://github.com/jashkenas/backbone/issues) for previous +discussions about the same feature or issue. Add to the earlier ticket if you +find one. + +* Before sending a pull request for a feature or bug fix, be sure to have +[tests](http://backbonejs.org/test/). + +* Use the same coding style as the rest of the +[codebase](https://github.com/jashkenas/backbone/blob/master/backbone.js). + +* In your pull request, do not add documentation or rebuild the minified +`backbone-min.js` file. We'll do that before cutting a new release. + +* All pull requests should be made to the `master` branch. diff --git a/vendor/assets/bower_components/backbone/LICENSE b/vendor/assets/bower_components/backbone/LICENSE new file mode 100644 index 0000000000..3ffd97de09 --- /dev/null +++ b/vendor/assets/bower_components/backbone/LICENSE @@ -0,0 +1,22 @@ +Copyright (c) 2010-2014 Jeremy Ashkenas, DocumentCloud + +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. diff --git a/vendor/assets/bower_components/backbone/README.md b/vendor/assets/bower_components/backbone/README.md new file mode 100644 index 0000000000..7b0482df70 --- /dev/null +++ b/vendor/assets/bower_components/backbone/README.md @@ -0,0 +1,29 @@ + ____ __ __ + /\ _`\ /\ \ /\ \ __ + \ \ \ \ \ __ ___\ \ \/'\\ \ \____ ___ ___ __ /\_\ ____ + \ \ _ <' /'__`\ /'___\ \ , < \ \ '__`\ / __`\ /' _ `\ /'__`\ \/\ \ /',__\ + \ \ \ \ \/\ \ \.\_/\ \__/\ \ \\`\\ \ \ \ \/\ \ \ \/\ \/\ \/\ __/ __ \ \ \/\__, `\ + \ \____/\ \__/.\_\ \____\\ \_\ \_\ \_,__/\ \____/\ \_\ \_\ \____\/\_\_\ \ \/\____/ + \/___/ \/__/\/_/\/____/ \/_/\/_/\/___/ \/___/ \/_/\/_/\/____/\/_/\ \_\ \/___/ + \ \____/ + \/___/ + (_'_______________________________________________________________________________'_) + (_.———————————————————————————————————————————————————————————————————————————————._) + + +Backbone supplies structure to JavaScript-heavy applications by providing models key-value binding and custom events, collections with a rich API of enumerable functions, views with declarative event handling, and connects it all to your existing application over a RESTful JSON interface. + +For Docs, License, Tests, pre-packed downloads, and everything else, really, see: +http://backbonejs.org + +To suggest a feature, report a bug, or general discussion: +http://github.com/jashkenas/backbone/issues + +Backbone is an open-sourced component of DocumentCloud: +https://github.com/documentcloud + +Many thanks to our contributors: +http://github.com/jashkenas/backbone/contributors + +Special thanks to Robert Kieffer for the original philosophy behind Backbone. +http://github.com/broofa diff --git a/vendor/assets/bower_components/backbone/backbone.js b/vendor/assets/bower_components/backbone/backbone.js new file mode 100644 index 0000000000..24a550a0ad --- /dev/null +++ b/vendor/assets/bower_components/backbone/backbone.js @@ -0,0 +1,1608 @@ +// Backbone.js 1.1.2 + +// (c) 2010-2014 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors +// Backbone may be freely distributed under the MIT license. +// For all details and documentation: +// http://backbonejs.org + +(function(root, factory) { + + // Set up Backbone appropriately for the environment. Start with AMD. + if (typeof define === 'function' && define.amd) { + define(['underscore', 'jquery', 'exports'], function(_, $, exports) { + // Export global even in AMD case in case this script is loaded with + // others that may still expect a global Backbone. + root.Backbone = factory(root, exports, _, $); + }); + + // Next for Node.js or CommonJS. jQuery may not be needed as a module. + } else if (typeof exports !== 'undefined') { + var _ = require('underscore'); + factory(root, exports, _); + + // Finally, as a browser global. + } else { + root.Backbone = factory(root, {}, root._, (root.jQuery || root.Zepto || root.ender || root.$)); + } + +}(this, function(root, Backbone, _, $) { + + // Initial Setup + // ------------- + + // Save the previous value of the `Backbone` variable, so that it can be + // restored later on, if `noConflict` is used. + var previousBackbone = root.Backbone; + + // Create local references to array methods we'll want to use later. + var array = []; + var push = array.push; + var slice = array.slice; + var splice = array.splice; + + // Current version of the library. Keep in sync with `package.json`. + Backbone.VERSION = '1.1.2'; + + // For Backbone's purposes, jQuery, Zepto, Ender, or My Library (kidding) owns + // the `$` variable. + Backbone.$ = $; + + // Runs Backbone.js in *noConflict* mode, returning the `Backbone` variable + // to its previous owner. Returns a reference to this Backbone object. + Backbone.noConflict = function() { + root.Backbone = previousBackbone; + return this; + }; + + // Turn on `emulateHTTP` to support legacy HTTP servers. Setting this option + // will fake `"PATCH"`, `"PUT"` and `"DELETE"` requests via the `_method` parameter and + // set a `X-Http-Method-Override` header. + Backbone.emulateHTTP = false; + + // Turn on `emulateJSON` to support legacy servers that can't deal with direct + // `application/json` requests ... will encode the body as + // `application/x-www-form-urlencoded` instead and will send the model in a + // form param named `model`. + Backbone.emulateJSON = false; + + // Backbone.Events + // --------------- + + // A module that can be mixed in to *any object* in order to provide it with + // custom events. You may bind with `on` or remove with `off` callback + // functions to an event; `trigger`-ing an event fires all callbacks in + // succession. + // + // var object = {}; + // _.extend(object, Backbone.Events); + // object.on('expand', function(){ alert('expanded'); }); + // object.trigger('expand'); + // + var Events = Backbone.Events = { + + // Bind an event to a `callback` function. Passing `"all"` will bind + // the callback to all events fired. + on: function(name, callback, context) { + if (!eventsApi(this, 'on', name, [callback, context]) || !callback) return this; + this._events || (this._events = {}); + var events = this._events[name] || (this._events[name] = []); + events.push({callback: callback, context: context, ctx: context || this}); + return this; + }, + + // Bind an event to only be triggered a single time. After the first time + // the callback is invoked, it will be removed. + once: function(name, callback, context) { + if (!eventsApi(this, 'once', name, [callback, context]) || !callback) return this; + var self = this; + var once = _.once(function() { + self.off(name, once); + callback.apply(this, arguments); + }); + once._callback = callback; + return this.on(name, once, context); + }, + + // Remove one or many callbacks. If `context` is null, removes all + // callbacks with that function. If `callback` is null, removes all + // callbacks for the event. If `name` is null, removes all bound + // callbacks for all events. + off: function(name, callback, context) { + var retain, ev, events, names, i, l, j, k; + if (!this._events || !eventsApi(this, 'off', name, [callback, context])) return this; + if (!name && !callback && !context) { + this._events = void 0; + return this; + } + names = name ? [name] : _.keys(this._events); + for (i = 0, l = names.length; i < l; i++) { + name = names[i]; + if (events = this._events[name]) { + this._events[name] = retain = []; + if (callback || context) { + for (j = 0, k = events.length; j < k; j++) { + ev = events[j]; + if ((callback && callback !== ev.callback && callback !== ev.callback._callback) || + (context && context !== ev.context)) { + retain.push(ev); + } + } + } + if (!retain.length) delete this._events[name]; + } + } + + return this; + }, + + // Trigger one or many events, firing all bound callbacks. Callbacks are + // passed the same arguments as `trigger` is, apart from the event name + // (unless you're listening on `"all"`, which will cause your callback to + // receive the true name of the event as the first argument). + trigger: function(name) { + if (!this._events) return this; + var args = slice.call(arguments, 1); + if (!eventsApi(this, 'trigger', name, args)) return this; + var events = this._events[name]; + var allEvents = this._events.all; + if (events) triggerEvents(events, args); + if (allEvents) triggerEvents(allEvents, arguments); + return this; + }, + + // Tell this object to stop listening to either specific events ... or + // to every object it's currently listening to. + stopListening: function(obj, name, callback) { + var listeningTo = this._listeningTo; + if (!listeningTo) return this; + var remove = !name && !callback; + if (!callback && typeof name === 'object') callback = this; + if (obj) (listeningTo = {})[obj._listenId] = obj; + for (var id in listeningTo) { + obj = listeningTo[id]; + obj.off(name, callback, this); + if (remove || _.isEmpty(obj._events)) delete this._listeningTo[id]; + } + return this; + } + + }; + + // Regular expression used to split event strings. + var eventSplitter = /\s+/; + + // Implement fancy features of the Events API such as multiple event + // names `"change blur"` and jQuery-style event maps `{change: action}` + // in terms of the existing API. + var eventsApi = function(obj, action, name, rest) { + if (!name) return true; + + // Handle event maps. + if (typeof name === 'object') { + for (var key in name) { + obj[action].apply(obj, [key, name[key]].concat(rest)); + } + return false; + } + + // Handle space separated event names. + if (eventSplitter.test(name)) { + var names = name.split(eventSplitter); + for (var i = 0, l = names.length; i < l; i++) { + obj[action].apply(obj, [names[i]].concat(rest)); + } + return false; + } + + return true; + }; + + // A difficult-to-believe, but optimized internal dispatch function for + // triggering events. Tries to keep the usual cases speedy (most internal + // Backbone events have 3 arguments). + var triggerEvents = function(events, args) { + var ev, i = -1, l = events.length, a1 = args[0], a2 = args[1], a3 = args[2]; + switch (args.length) { + case 0: while (++i < l) (ev = events[i]).callback.call(ev.ctx); return; + case 1: while (++i < l) (ev = events[i]).callback.call(ev.ctx, a1); return; + case 2: while (++i < l) (ev = events[i]).callback.call(ev.ctx, a1, a2); return; + case 3: while (++i < l) (ev = events[i]).callback.call(ev.ctx, a1, a2, a3); return; + default: while (++i < l) (ev = events[i]).callback.apply(ev.ctx, args); return; + } + }; + + var listenMethods = {listenTo: 'on', listenToOnce: 'once'}; + + // Inversion-of-control versions of `on` and `once`. Tell *this* object to + // listen to an event in another object ... keeping track of what it's + // listening to. + _.each(listenMethods, function(implementation, method) { + Events[method] = function(obj, name, callback) { + var listeningTo = this._listeningTo || (this._listeningTo = {}); + var id = obj._listenId || (obj._listenId = _.uniqueId('l')); + listeningTo[id] = obj; + if (!callback && typeof name === 'object') callback = this; + obj[implementation](name, callback, this); + return this; + }; + }); + + // Aliases for backwards compatibility. + Events.bind = Events.on; + Events.unbind = Events.off; + + // Allow the `Backbone` object to serve as a global event bus, for folks who + // want global "pubsub" in a convenient place. + _.extend(Backbone, Events); + + // Backbone.Model + // -------------- + + // Backbone **Models** are the basic data object in the framework -- + // frequently representing a row in a table in a database on your server. + // A discrete chunk of data and a bunch of useful, related methods for + // performing computations and transformations on that data. + + // Create a new model with the specified attributes. A client id (`cid`) + // is automatically generated and assigned for you. + var Model = Backbone.Model = function(attributes, options) { + var attrs = attributes || {}; + options || (options = {}); + this.cid = _.uniqueId('c'); + this.attributes = {}; + if (options.collection) this.collection = options.collection; + if (options.parse) attrs = this.parse(attrs, options) || {}; + attrs = _.defaults({}, attrs, _.result(this, 'defaults')); + this.set(attrs, options); + this.changed = {}; + this.initialize.apply(this, arguments); + }; + + // Attach all inheritable methods to the Model prototype. + _.extend(Model.prototype, Events, { + + // A hash of attributes whose current and previous value differ. + changed: null, + + // The value returned during the last failed validation. + validationError: null, + + // The default name for the JSON `id` attribute is `"id"`. MongoDB and + // CouchDB users may want to set this to `"_id"`. + idAttribute: 'id', + + // Initialize is an empty function by default. Override it with your own + // initialization logic. + initialize: function(){}, + + // Return a copy of the model's `attributes` object. + toJSON: function(options) { + return _.clone(this.attributes); + }, + + // Proxy `Backbone.sync` by default -- but override this if you need + // custom syncing semantics for *this* particular model. + sync: function() { + return Backbone.sync.apply(this, arguments); + }, + + // Get the value of an attribute. + get: function(attr) { + return this.attributes[attr]; + }, + + // Get the HTML-escaped value of an attribute. + escape: function(attr) { + return _.escape(this.get(attr)); + }, + + // Returns `true` if the attribute contains a value that is not null + // or undefined. + has: function(attr) { + return this.get(attr) != null; + }, + + // Set a hash of model attributes on the object, firing `"change"`. This is + // the core primitive operation of a model, updating the data and notifying + // anyone who needs to know about the change in state. The heart of the beast. + set: function(key, val, options) { + var attr, attrs, unset, changes, silent, changing, prev, current; + if (key == null) return this; + + // Handle both `"key", value` and `{key: value}` -style arguments. + if (typeof key === 'object') { + attrs = key; + options = val; + } else { + (attrs = {})[key] = val; + } + + options || (options = {}); + + // Run validation. + if (!this._validate(attrs, options)) return false; + + // Extract attributes and options. + unset = options.unset; + silent = options.silent; + changes = []; + changing = this._changing; + this._changing = true; + + if (!changing) { + this._previousAttributes = _.clone(this.attributes); + this.changed = {}; + } + current = this.attributes, prev = this._previousAttributes; + + // Check for changes of `id`. + if (this.idAttribute in attrs) this.id = attrs[this.idAttribute]; + + // For each `set` attribute, update or delete the current value. + for (attr in attrs) { + val = attrs[attr]; + if (!_.isEqual(current[attr], val)) changes.push(attr); + if (!_.isEqual(prev[attr], val)) { + this.changed[attr] = val; + } else { + delete this.changed[attr]; + } + unset ? delete current[attr] : current[attr] = val; + } + + // Trigger all relevant attribute changes. + if (!silent) { + if (changes.length) this._pending = options; + for (var i = 0, l = changes.length; i < l; i++) { + this.trigger('change:' + changes[i], this, current[changes[i]], options); + } + } + + // You might be wondering why there's a `while` loop here. Changes can + // be recursively nested within `"change"` events. + if (changing) return this; + if (!silent) { + while (this._pending) { + options = this._pending; + this._pending = false; + this.trigger('change', this, options); + } + } + this._pending = false; + this._changing = false; + return this; + }, + + // Remove an attribute from the model, firing `"change"`. `unset` is a noop + // if the attribute doesn't exist. + unset: function(attr, options) { + return this.set(attr, void 0, _.extend({}, options, {unset: true})); + }, + + // Clear all attributes on the model, firing `"change"`. + clear: function(options) { + var attrs = {}; + for (var key in this.attributes) attrs[key] = void 0; + return this.set(attrs, _.extend({}, options, {unset: true})); + }, + + // Determine if the model has changed since the last `"change"` event. + // If you specify an attribute name, determine if that attribute has changed. + hasChanged: function(attr) { + if (attr == null) return !_.isEmpty(this.changed); + return _.has(this.changed, attr); + }, + + // Return an object containing all the attributes that have changed, or + // false if there are no changed attributes. Useful for determining what + // parts of a view need to be updated and/or what attributes need to be + // persisted to the server. Unset attributes will be set to undefined. + // You can also pass an attributes object to diff against the model, + // determining if there *would be* a change. + changedAttributes: function(diff) { + if (!diff) return this.hasChanged() ? _.clone(this.changed) : false; + var val, changed = false; + var old = this._changing ? this._previousAttributes : this.attributes; + for (var attr in diff) { + if (_.isEqual(old[attr], (val = diff[attr]))) continue; + (changed || (changed = {}))[attr] = val; + } + return changed; + }, + + // Get the previous value of an attribute, recorded at the time the last + // `"change"` event was fired. + previous: function(attr) { + if (attr == null || !this._previousAttributes) return null; + return this._previousAttributes[attr]; + }, + + // Get all of the attributes of the model at the time of the previous + // `"change"` event. + previousAttributes: function() { + return _.clone(this._previousAttributes); + }, + + // Fetch the model from the server. If the server's representation of the + // model differs from its current attributes, they will be overridden, + // triggering a `"change"` event. + fetch: function(options) { + options = options ? _.clone(options) : {}; + if (options.parse === void 0) options.parse = true; + var model = this; + var success = options.success; + options.success = function(resp) { + if (!model.set(model.parse(resp, options), options)) return false; + if (success) success(model, resp, options); + model.trigger('sync', model, resp, options); + }; + wrapError(this, options); + return this.sync('read', this, options); + }, + + // Set a hash of model attributes, and sync the model to the server. + // If the server returns an attributes hash that differs, the model's + // state will be `set` again. + save: function(key, val, options) { + var attrs, method, xhr, attributes = this.attributes; + + // Handle both `"key", value` and `{key: value}` -style arguments. + if (key == null || typeof key === 'object') { + attrs = key; + options = val; + } else { + (attrs = {})[key] = val; + } + + options = _.extend({validate: true}, options); + + // If we're not waiting and attributes exist, save acts as + // `set(attr).save(null, opts)` with validation. Otherwise, check if + // the model will be valid when the attributes, if any, are set. + if (attrs && !options.wait) { + if (!this.set(attrs, options)) return false; + } else { + if (!this._validate(attrs, options)) return false; + } + + // Set temporary attributes if `{wait: true}`. + if (attrs && options.wait) { + this.attributes = _.extend({}, attributes, attrs); + } + + // After a successful server-side save, the client is (optionally) + // updated with the server-side state. + if (options.parse === void 0) options.parse = true; + var model = this; + var success = options.success; + options.success = function(resp) { + // Ensure attributes are restored during synchronous saves. + model.attributes = attributes; + var serverAttrs = model.parse(resp, options); + if (options.wait) serverAttrs = _.extend(attrs || {}, serverAttrs); + if (_.isObject(serverAttrs) && !model.set(serverAttrs, options)) { + return false; + } + if (success) success(model, resp, options); + model.trigger('sync', model, resp, options); + }; + wrapError(this, options); + + method = this.isNew() ? 'create' : (options.patch ? 'patch' : 'update'); + if (method === 'patch') options.attrs = attrs; + xhr = this.sync(method, this, options); + + // Restore attributes. + if (attrs && options.wait) this.attributes = attributes; + + return xhr; + }, + + // Destroy this model on the server if it was already persisted. + // Optimistically removes the model from its collection, if it has one. + // If `wait: true` is passed, waits for the server to respond before removal. + destroy: function(options) { + options = options ? _.clone(options) : {}; + var model = this; + var success = options.success; + + var destroy = function() { + model.trigger('destroy', model, model.collection, options); + }; + + options.success = function(resp) { + if (options.wait || model.isNew()) destroy(); + if (success) success(model, resp, options); + if (!model.isNew()) model.trigger('sync', model, resp, options); + }; + + if (this.isNew()) { + options.success(); + return false; + } + wrapError(this, options); + + var xhr = this.sync('delete', this, options); + if (!options.wait) destroy(); + return xhr; + }, + + // Default URL for the model's representation on the server -- if you're + // using Backbone's restful methods, override this to change the endpoint + // that will be called. + url: function() { + var base = + _.result(this, 'urlRoot') || + _.result(this.collection, 'url') || + urlError(); + if (this.isNew()) return base; + return base.replace(/([^\/])$/, '$1/') + encodeURIComponent(this.id); + }, + + // **parse** converts a response into the hash of attributes to be `set` on + // the model. The default implementation is just to pass the response along. + parse: function(resp, options) { + return resp; + }, + + // Create a new model with identical attributes to this one. + clone: function() { + return new this.constructor(this.attributes); + }, + + // A model is new if it has never been saved to the server, and lacks an id. + isNew: function() { + return !this.has(this.idAttribute); + }, + + // Check if the model is currently in a valid state. + isValid: function(options) { + return this._validate({}, _.extend(options || {}, { validate: true })); + }, + + // Run validation against the next complete set of model attributes, + // returning `true` if all is well. Otherwise, fire an `"invalid"` event. + _validate: function(attrs, options) { + if (!options.validate || !this.validate) return true; + attrs = _.extend({}, this.attributes, attrs); + var error = this.validationError = this.validate(attrs, options) || null; + if (!error) return true; + this.trigger('invalid', this, error, _.extend(options, {validationError: error})); + return false; + } + + }); + + // Underscore methods that we want to implement on the Model. + var modelMethods = ['keys', 'values', 'pairs', 'invert', 'pick', 'omit']; + + // Mix in each Underscore method as a proxy to `Model#attributes`. + _.each(modelMethods, function(method) { + Model.prototype[method] = function() { + var args = slice.call(arguments); + args.unshift(this.attributes); + return _[method].apply(_, args); + }; + }); + + // Backbone.Collection + // ------------------- + + // If models tend to represent a single row of data, a Backbone Collection is + // more analagous to a table full of data ... or a small slice or page of that + // table, or a collection of rows that belong together for a particular reason + // -- all of the messages in this particular folder, all of the documents + // belonging to this particular author, and so on. Collections maintain + // indexes of their models, both in order, and for lookup by `id`. + + // Create a new **Collection**, perhaps to contain a specific type of `model`. + // If a `comparator` is specified, the Collection will maintain + // its models in sort order, as they're added and removed. + var Collection = Backbone.Collection = function(models, options) { + options || (options = {}); + if (options.model) this.model = options.model; + if (options.comparator !== void 0) this.comparator = options.comparator; + this._reset(); + this.initialize.apply(this, arguments); + if (models) this.reset(models, _.extend({silent: true}, options)); + }; + + // Default options for `Collection#set`. + var setOptions = {add: true, remove: true, merge: true}; + var addOptions = {add: true, remove: false}; + + // Define the Collection's inheritable methods. + _.extend(Collection.prototype, Events, { + + // The default model for a collection is just a **Backbone.Model**. + // This should be overridden in most cases. + model: Model, + + // Initialize is an empty function by default. Override it with your own + // initialization logic. + initialize: function(){}, + + // The JSON representation of a Collection is an array of the + // models' attributes. + toJSON: function(options) { + return this.map(function(model){ return model.toJSON(options); }); + }, + + // Proxy `Backbone.sync` by default. + sync: function() { + return Backbone.sync.apply(this, arguments); + }, + + // Add a model, or list of models to the set. + add: function(models, options) { + return this.set(models, _.extend({merge: false}, options, addOptions)); + }, + + // Remove a model, or a list of models from the set. + remove: function(models, options) { + var singular = !_.isArray(models); + models = singular ? [models] : _.clone(models); + options || (options = {}); + var i, l, index, model; + for (i = 0, l = models.length; i < l; i++) { + model = models[i] = this.get(models[i]); + if (!model) continue; + delete this._byId[model.id]; + delete this._byId[model.cid]; + index = this.indexOf(model); + this.models.splice(index, 1); + this.length--; + if (!options.silent) { + options.index = index; + model.trigger('remove', model, this, options); + } + this._removeReference(model, options); + } + return singular ? models[0] : models; + }, + + // Update a collection by `set`-ing a new list of models, adding new ones, + // removing models that are no longer present, and merging models that + // already exist in the collection, as necessary. Similar to **Model#set**, + // the core operation for updating the data contained by the collection. + set: function(models, options) { + options = _.defaults({}, options, setOptions); + if (options.parse) models = this.parse(models, options); + var singular = !_.isArray(models); + models = singular ? (models ? [models] : []) : _.clone(models); + var i, l, id, model, attrs, existing, sort; + var at = options.at; + var targetModel = this.model; + var sortable = this.comparator && (at == null) && options.sort !== false; + var sortAttr = _.isString(this.comparator) ? this.comparator : null; + var toAdd = [], toRemove = [], modelMap = {}; + var add = options.add, merge = options.merge, remove = options.remove; + var order = !sortable && add && remove ? [] : false; + + // Turn bare objects into model references, and prevent invalid models + // from being added. + for (i = 0, l = models.length; i < l; i++) { + attrs = models[i] || {}; + if (attrs instanceof Model) { + id = model = attrs; + } else { + id = attrs[targetModel.prototype.idAttribute || 'id']; + } + + // If a duplicate is found, prevent it from being added and + // optionally merge it into the existing model. + if (existing = this.get(id)) { + if (remove) modelMap[existing.cid] = true; + if (merge) { + attrs = attrs === model ? model.attributes : attrs; + if (options.parse) attrs = existing.parse(attrs, options); + existing.set(attrs, options); + if (sortable && !sort && existing.hasChanged(sortAttr)) sort = true; + } + models[i] = existing; + + // If this is a new, valid model, push it to the `toAdd` list. + } else if (add) { + model = models[i] = this._prepareModel(attrs, options); + if (!model) continue; + toAdd.push(model); + this._addReference(model, options); + } + + // Do not add multiple models with the same `id`. + model = existing || model; + if (order && (model.isNew() || !modelMap[model.id])) order.push(model); + modelMap[model.id] = true; + } + + // Remove nonexistent models if appropriate. + if (remove) { + for (i = 0, l = this.length; i < l; ++i) { + if (!modelMap[(model = this.models[i]).cid]) toRemove.push(model); + } + if (toRemove.length) this.remove(toRemove, options); + } + + // See if sorting is needed, update `length` and splice in new models. + if (toAdd.length || (order && order.length)) { + if (sortable) sort = true; + this.length += toAdd.length; + if (at != null) { + for (i = 0, l = toAdd.length; i < l; i++) { + this.models.splice(at + i, 0, toAdd[i]); + } + } else { + if (order) this.models.length = 0; + var orderedModels = order || toAdd; + for (i = 0, l = orderedModels.length; i < l; i++) { + this.models.push(orderedModels[i]); + } + } + } + + // Silently sort the collection if appropriate. + if (sort) this.sort({silent: true}); + + // Unless silenced, it's time to fire all appropriate add/sort events. + if (!options.silent) { + for (i = 0, l = toAdd.length; i < l; i++) { + (model = toAdd[i]).trigger('add', model, this, options); + } + if (sort || (order && order.length)) this.trigger('sort', this, options); + } + + // Return the added (or merged) model (or models). + return singular ? models[0] : models; + }, + + // When you have more items than you want to add or remove individually, + // you can reset the entire set with a new list of models, without firing + // any granular `add` or `remove` events. Fires `reset` when finished. + // Useful for bulk operations and optimizations. + reset: function(models, options) { + options || (options = {}); + for (var i = 0, l = this.models.length; i < l; i++) { + this._removeReference(this.models[i], options); + } + options.previousModels = this.models; + this._reset(); + models = this.add(models, _.extend({silent: true}, options)); + if (!options.silent) this.trigger('reset', this, options); + return models; + }, + + // Add a model to the end of the collection. + push: function(model, options) { + return this.add(model, _.extend({at: this.length}, options)); + }, + + // Remove a model from the end of the collection. + pop: function(options) { + var model = this.at(this.length - 1); + this.remove(model, options); + return model; + }, + + // Add a model to the beginning of the collection. + unshift: function(model, options) { + return this.add(model, _.extend({at: 0}, options)); + }, + + // Remove a model from the beginning of the collection. + shift: function(options) { + var model = this.at(0); + this.remove(model, options); + return model; + }, + + // Slice out a sub-array of models from the collection. + slice: function() { + return slice.apply(this.models, arguments); + }, + + // Get a model from the set by id. + get: function(obj) { + if (obj == null) return void 0; + return this._byId[obj] || this._byId[obj.id] || this._byId[obj.cid]; + }, + + // Get the model at the given index. + at: function(index) { + return this.models[index]; + }, + + // Return models with matching attributes. Useful for simple cases of + // `filter`. + where: function(attrs, first) { + if (_.isEmpty(attrs)) return first ? void 0 : []; + return this[first ? 'find' : 'filter'](function(model) { + for (var key in attrs) { + if (attrs[key] !== model.get(key)) return false; + } + return true; + }); + }, + + // Return the first model with matching attributes. Useful for simple cases + // of `find`. + findWhere: function(attrs) { + return this.where(attrs, true); + }, + + // Force the collection to re-sort itself. You don't need to call this under + // normal circumstances, as the set will maintain sort order as each item + // is added. + sort: function(options) { + if (!this.comparator) throw new Error('Cannot sort a set without a comparator'); + options || (options = {}); + + // Run sort based on type of `comparator`. + if (_.isString(this.comparator) || this.comparator.length === 1) { + this.models = this.sortBy(this.comparator, this); + } else { + this.models.sort(_.bind(this.comparator, this)); + } + + if (!options.silent) this.trigger('sort', this, options); + return this; + }, + + // Pluck an attribute from each model in the collection. + pluck: function(attr) { + return _.invoke(this.models, 'get', attr); + }, + + // Fetch the default set of models for this collection, resetting the + // collection when they arrive. If `reset: true` is passed, the response + // data will be passed through the `reset` method instead of `set`. + fetch: function(options) { + options = options ? _.clone(options) : {}; + if (options.parse === void 0) options.parse = true; + var success = options.success; + var collection = this; + options.success = function(resp) { + var method = options.reset ? 'reset' : 'set'; + collection[method](resp, options); + if (success) success(collection, resp, options); + collection.trigger('sync', collection, resp, options); + }; + wrapError(this, options); + return this.sync('read', this, options); + }, + + // Create a new instance of a model in this collection. Add the model to the + // collection immediately, unless `wait: true` is passed, in which case we + // wait for the server to agree. + create: function(model, options) { + options = options ? _.clone(options) : {}; + if (!(model = this._prepareModel(model, options))) return false; + if (!options.wait) this.add(model, options); + var collection = this; + var success = options.success; + options.success = function(model, resp) { + if (options.wait) collection.add(model, options); + if (success) success(model, resp, options); + }; + model.save(null, options); + return model; + }, + + // **parse** converts a response into a list of models to be added to the + // collection. The default implementation is just to pass it through. + parse: function(resp, options) { + return resp; + }, + + // Create a new collection with an identical list of models as this one. + clone: function() { + return new this.constructor(this.models); + }, + + // Private method to reset all internal state. Called when the collection + // is first initialized or reset. + _reset: function() { + this.length = 0; + this.models = []; + this._byId = {}; + }, + + // Prepare a hash of attributes (or other model) to be added to this + // collection. + _prepareModel: function(attrs, options) { + if (attrs instanceof Model) return attrs; + options = options ? _.clone(options) : {}; + options.collection = this; + var model = new this.model(attrs, options); + if (!model.validationError) return model; + this.trigger('invalid', this, model.validationError, options); + return false; + }, + + // Internal method to create a model's ties to a collection. + _addReference: function(model, options) { + this._byId[model.cid] = model; + if (model.id != null) this._byId[model.id] = model; + if (!model.collection) model.collection = this; + model.on('all', this._onModelEvent, this); + }, + + // Internal method to sever a model's ties to a collection. + _removeReference: function(model, options) { + if (this === model.collection) delete model.collection; + model.off('all', this._onModelEvent, this); + }, + + // Internal method called every time a model in the set fires an event. + // Sets need to update their indexes when models change ids. All other + // events simply proxy through. "add" and "remove" events that originate + // in other collections are ignored. + _onModelEvent: function(event, model, collection, options) { + if ((event === 'add' || event === 'remove') && collection !== this) return; + if (event === 'destroy') this.remove(model, options); + if (model && event === 'change:' + model.idAttribute) { + delete this._byId[model.previous(model.idAttribute)]; + if (model.id != null) this._byId[model.id] = model; + } + this.trigger.apply(this, arguments); + } + + }); + + // Underscore methods that we want to implement on the Collection. + // 90% of the core usefulness of Backbone Collections is actually implemented + // right here: + var methods = ['forEach', 'each', 'map', 'collect', 'reduce', 'foldl', + 'inject', 'reduceRight', 'foldr', 'find', 'detect', 'filter', 'select', + 'reject', 'every', 'all', 'some', 'any', 'include', 'contains', 'invoke', + 'max', 'min', 'toArray', 'size', 'first', 'head', 'take', 'initial', 'rest', + 'tail', 'drop', 'last', 'without', 'difference', 'indexOf', 'shuffle', + 'lastIndexOf', 'isEmpty', 'chain', 'sample']; + + // Mix in each Underscore method as a proxy to `Collection#models`. + _.each(methods, function(method) { + Collection.prototype[method] = function() { + var args = slice.call(arguments); + args.unshift(this.models); + return _[method].apply(_, args); + }; + }); + + // Underscore methods that take a property name as an argument. + var attributeMethods = ['groupBy', 'countBy', 'sortBy', 'indexBy']; + + // Use attributes instead of properties. + _.each(attributeMethods, function(method) { + Collection.prototype[method] = function(value, context) { + var iterator = _.isFunction(value) ? value : function(model) { + return model.get(value); + }; + return _[method](this.models, iterator, context); + }; + }); + + // Backbone.View + // ------------- + + // Backbone Views are almost more convention than they are actual code. A View + // is simply a JavaScript object that represents a logical chunk of UI in the + // DOM. This might be a single item, an entire list, a sidebar or panel, or + // even the surrounding frame which wraps your whole app. Defining a chunk of + // UI as a **View** allows you to define your DOM events declaratively, without + // having to worry about render order ... and makes it easy for the view to + // react to specific changes in the state of your models. + + // Creating a Backbone.View creates its initial element outside of the DOM, + // if an existing element is not provided... + var View = Backbone.View = function(options) { + this.cid = _.uniqueId('view'); + options || (options = {}); + _.extend(this, _.pick(options, viewOptions)); + this._ensureElement(); + this.initialize.apply(this, arguments); + this.delegateEvents(); + }; + + // Cached regex to split keys for `delegate`. + var delegateEventSplitter = /^(\S+)\s*(.*)$/; + + // List of view options to be merged as properties. + var viewOptions = ['model', 'collection', 'el', 'id', 'attributes', 'className', 'tagName', 'events']; + + // Set up all inheritable **Backbone.View** properties and methods. + _.extend(View.prototype, Events, { + + // The default `tagName` of a View's element is `"div"`. + tagName: 'div', + + // jQuery delegate for element lookup, scoped to DOM elements within the + // current view. This should be preferred to global lookups where possible. + $: function(selector) { + return this.$el.find(selector); + }, + + // Initialize is an empty function by default. Override it with your own + // initialization logic. + initialize: function(){}, + + // **render** is the core function that your view should override, in order + // to populate its element (`this.el`), with the appropriate HTML. The + // convention is for **render** to always return `this`. + render: function() { + return this; + }, + + // Remove this view by taking the element out of the DOM, and removing any + // applicable Backbone.Events listeners. + remove: function() { + this.$el.remove(); + this.stopListening(); + return this; + }, + + // Change the view's element (`this.el` property), including event + // re-delegation. + setElement: function(element, delegate) { + if (this.$el) this.undelegateEvents(); + this.$el = element instanceof Backbone.$ ? element : Backbone.$(element); + this.el = this.$el[0]; + if (delegate !== false) this.delegateEvents(); + return this; + }, + + // Set callbacks, where `this.events` is a hash of + // + // *{"event selector": "callback"}* + // + // { + // 'mousedown .title': 'edit', + // 'click .button': 'save', + // 'click .open': function(e) { ... } + // } + // + // pairs. Callbacks will be bound to the view, with `this` set properly. + // Uses event delegation for efficiency. + // Omitting the selector binds the event to `this.el`. + // This only works for delegate-able events: not `focus`, `blur`, and + // not `change`, `submit`, and `reset` in Internet Explorer. + delegateEvents: function(events) { + if (!(events || (events = _.result(this, 'events')))) return this; + this.undelegateEvents(); + for (var key in events) { + var method = events[key]; + if (!_.isFunction(method)) method = this[events[key]]; + if (!method) continue; + + var match = key.match(delegateEventSplitter); + var eventName = match[1], selector = match[2]; + method = _.bind(method, this); + eventName += '.delegateEvents' + this.cid; + if (selector === '') { + this.$el.on(eventName, method); + } else { + this.$el.on(eventName, selector, method); + } + } + return this; + }, + + // Clears all callbacks previously bound to the view with `delegateEvents`. + // You usually don't need to use this, but may wish to if you have multiple + // Backbone views attached to the same DOM element. + undelegateEvents: function() { + this.$el.off('.delegateEvents' + this.cid); + return this; + }, + + // Ensure that the View has a DOM element to render into. + // If `this.el` is a string, pass it through `$()`, take the first + // matching element, and re-assign it to `el`. Otherwise, create + // an element from the `id`, `className` and `tagName` properties. + _ensureElement: function() { + if (!this.el) { + var attrs = _.extend({}, _.result(this, 'attributes')); + if (this.id) attrs.id = _.result(this, 'id'); + if (this.className) attrs['class'] = _.result(this, 'className'); + var $el = Backbone.$('<' + _.result(this, 'tagName') + '>').attr(attrs); + this.setElement($el, false); + } else { + this.setElement(_.result(this, 'el'), false); + } + } + + }); + + // Backbone.sync + // ------------- + + // Override this function to change the manner in which Backbone persists + // models to the server. You will be passed the type of request, and the + // model in question. By default, makes a RESTful Ajax request + // to the model's `url()`. Some possible customizations could be: + // + // * Use `setTimeout` to batch rapid-fire updates into a single request. + // * Send up the models as XML instead of JSON. + // * Persist models via WebSockets instead of Ajax. + // + // Turn on `Backbone.emulateHTTP` in order to send `PUT` and `DELETE` requests + // as `POST`, with a `_method` parameter containing the true HTTP method, + // as well as all requests with the body as `application/x-www-form-urlencoded` + // instead of `application/json` with the model in a param named `model`. + // Useful when interfacing with server-side languages like **PHP** that make + // it difficult to read the body of `PUT` requests. + Backbone.sync = function(method, model, options) { + var type = methodMap[method]; + + // Default options, unless specified. + _.defaults(options || (options = {}), { + emulateHTTP: Backbone.emulateHTTP, + emulateJSON: Backbone.emulateJSON + }); + + // Default JSON-request options. + var params = {type: type, dataType: 'json'}; + + // Ensure that we have a URL. + if (!options.url) { + params.url = _.result(model, 'url') || urlError(); + } + + // Ensure that we have the appropriate request data. + if (options.data == null && model && (method === 'create' || method === 'update' || method === 'patch')) { + params.contentType = 'application/json'; + params.data = JSON.stringify(options.attrs || model.toJSON(options)); + } + + // For older servers, emulate JSON by encoding the request into an HTML-form. + if (options.emulateJSON) { + params.contentType = 'application/x-www-form-urlencoded'; + params.data = params.data ? {model: params.data} : {}; + } + + // For older servers, emulate HTTP by mimicking the HTTP method with `_method` + // And an `X-HTTP-Method-Override` header. + if (options.emulateHTTP && (type === 'PUT' || type === 'DELETE' || type === 'PATCH')) { + params.type = 'POST'; + if (options.emulateJSON) params.data._method = type; + var beforeSend = options.beforeSend; + options.beforeSend = function(xhr) { + xhr.setRequestHeader('X-HTTP-Method-Override', type); + if (beforeSend) return beforeSend.apply(this, arguments); + }; + } + + // Don't process data on a non-GET request. + if (params.type !== 'GET' && !options.emulateJSON) { + params.processData = false; + } + + // If we're sending a `PATCH` request, and we're in an old Internet Explorer + // that still has ActiveX enabled by default, override jQuery to use that + // for XHR instead. Remove this line when jQuery supports `PATCH` on IE8. + if (params.type === 'PATCH' && noXhrPatch) { + params.xhr = function() { + return new ActiveXObject("Microsoft.XMLHTTP"); + }; + } + + // Make the request, allowing the user to override any Ajax options. + var xhr = options.xhr = Backbone.ajax(_.extend(params, options)); + model.trigger('request', model, xhr, options); + return xhr; + }; + + var noXhrPatch = + typeof window !== 'undefined' && !!window.ActiveXObject && + !(window.XMLHttpRequest && (new XMLHttpRequest).dispatchEvent); + + // Map from CRUD to HTTP for our default `Backbone.sync` implementation. + var methodMap = { + 'create': 'POST', + 'update': 'PUT', + 'patch': 'PATCH', + 'delete': 'DELETE', + 'read': 'GET' + }; + + // Set the default implementation of `Backbone.ajax` to proxy through to `$`. + // Override this if you'd like to use a different library. + Backbone.ajax = function() { + return Backbone.$.ajax.apply(Backbone.$, arguments); + }; + + // Backbone.Router + // --------------- + + // Routers map faux-URLs to actions, and fire events when routes are + // matched. Creating a new one sets its `routes` hash, if not set statically. + var Router = Backbone.Router = function(options) { + options || (options = {}); + if (options.routes) this.routes = options.routes; + this._bindRoutes(); + this.initialize.apply(this, arguments); + }; + + // Cached regular expressions for matching named param parts and splatted + // parts of route strings. + var optionalParam = /\((.*?)\)/g; + var namedParam = /(\(\?)?:\w+/g; + var splatParam = /\*\w+/g; + var escapeRegExp = /[\-{}\[\]+?.,\\\^$|#\s]/g; + + // Set up all inheritable **Backbone.Router** properties and methods. + _.extend(Router.prototype, Events, { + + // Initialize is an empty function by default. Override it with your own + // initialization logic. + initialize: function(){}, + + // Manually bind a single named route to a callback. For example: + // + // this.route('search/:query/p:num', 'search', function(query, num) { + // ... + // }); + // + route: function(route, name, callback) { + if (!_.isRegExp(route)) route = this._routeToRegExp(route); + if (_.isFunction(name)) { + callback = name; + name = ''; + } + if (!callback) callback = this[name]; + var router = this; + Backbone.history.route(route, function(fragment) { + var args = router._extractParameters(route, fragment); + router.execute(callback, args); + router.trigger.apply(router, ['route:' + name].concat(args)); + router.trigger('route', name, args); + Backbone.history.trigger('route', router, name, args); + }); + return this; + }, + + // Execute a route handler with the provided parameters. This is an + // excellent place to do pre-route setup or post-route cleanup. + execute: function(callback, args) { + if (callback) callback.apply(this, args); + }, + + // Simple proxy to `Backbone.history` to save a fragment into the history. + navigate: function(fragment, options) { + Backbone.history.navigate(fragment, options); + return this; + }, + + // Bind all defined routes to `Backbone.history`. We have to reverse the + // order of the routes here to support behavior where the most general + // routes can be defined at the bottom of the route map. + _bindRoutes: function() { + if (!this.routes) return; + this.routes = _.result(this, 'routes'); + var route, routes = _.keys(this.routes); + while ((route = routes.pop()) != null) { + this.route(route, this.routes[route]); + } + }, + + // Convert a route string into a regular expression, suitable for matching + // against the current location hash. + _routeToRegExp: function(route) { + route = route.replace(escapeRegExp, '\\$&') + .replace(optionalParam, '(?:$1)?') + .replace(namedParam, function(match, optional) { + return optional ? match : '([^/?]+)'; + }) + .replace(splatParam, '([^?]*?)'); + return new RegExp('^' + route + '(?:\\?([\\s\\S]*))?$'); + }, + + // Given a route, and a URL fragment that it matches, return the array of + // extracted decoded parameters. Empty or unmatched parameters will be + // treated as `null` to normalize cross-browser behavior. + _extractParameters: function(route, fragment) { + var params = route.exec(fragment).slice(1); + return _.map(params, function(param, i) { + // Don't decode the search params. + if (i === params.length - 1) return param || null; + return param ? decodeURIComponent(param) : null; + }); + } + + }); + + // Backbone.History + // ---------------- + + // Handles cross-browser history management, based on either + // [pushState](http://diveintohtml5.info/history.html) and real URLs, or + // [onhashchange](https://developer.mozilla.org/en-US/docs/DOM/window.onhashchange) + // and URL fragments. If the browser supports neither (old IE, natch), + // falls back to polling. + var History = Backbone.History = function() { + this.handlers = []; + _.bindAll(this, 'checkUrl'); + + // Ensure that `History` can be used outside of the browser. + if (typeof window !== 'undefined') { + this.location = window.location; + this.history = window.history; + } + }; + + // Cached regex for stripping a leading hash/slash and trailing space. + var routeStripper = /^[#\/]|\s+$/g; + + // Cached regex for stripping leading and trailing slashes. + var rootStripper = /^\/+|\/+$/g; + + // Cached regex for detecting MSIE. + var isExplorer = /msie [\w.]+/; + + // Cached regex for removing a trailing slash. + var trailingSlash = /\/$/; + + // Cached regex for stripping urls of hash. + var pathStripper = /#.*$/; + + // Has the history handling already been started? + History.started = false; + + // Set up all inheritable **Backbone.History** properties and methods. + _.extend(History.prototype, Events, { + + // The default interval to poll for hash changes, if necessary, is + // twenty times a second. + interval: 50, + + // Are we at the app root? + atRoot: function() { + return this.location.pathname.replace(/[^\/]$/, '$&/') === this.root; + }, + + // Gets the true hash value. Cannot use location.hash directly due to bug + // in Firefox where location.hash will always be decoded. + getHash: function(window) { + var match = (window || this).location.href.match(/#(.*)$/); + return match ? match[1] : ''; + }, + + // Get the cross-browser normalized URL fragment, either from the URL, + // the hash, or the override. + getFragment: function(fragment, forcePushState) { + if (fragment == null) { + if (this._hasPushState || !this._wantsHashChange || forcePushState) { + fragment = decodeURI(this.location.pathname + this.location.search); + var root = this.root.replace(trailingSlash, ''); + if (!fragment.indexOf(root)) fragment = fragment.slice(root.length); + } else { + fragment = this.getHash(); + } + } + return fragment.replace(routeStripper, ''); + }, + + // Start the hash change handling, returning `true` if the current URL matches + // an existing route, and `false` otherwise. + start: function(options) { + if (History.started) throw new Error("Backbone.history has already been started"); + History.started = true; + + // Figure out the initial configuration. Do we need an iframe? + // Is pushState desired ... is it available? + this.options = _.extend({root: '/'}, this.options, options); + this.root = this.options.root; + this._wantsHashChange = this.options.hashChange !== false; + this._wantsPushState = !!this.options.pushState; + this._hasPushState = !!(this.options.pushState && this.history && this.history.pushState); + var fragment = this.getFragment(); + var docMode = document.documentMode; + var oldIE = (isExplorer.exec(navigator.userAgent.toLowerCase()) && (!docMode || docMode <= 7)); + + // Normalize root to always include a leading and trailing slash. + this.root = ('/' + this.root + '/').replace(rootStripper, '/'); + + if (oldIE && this._wantsHashChange) { + var frame = Backbone.$('"; + + var dialog = new cdb.ui.common.ShareDialog({ + title: data.map.get("title"), + description: data.map.get("description"), + model: vis.map, + code: code, + url: data.url, + public_map_url: public_map_url, + share_url: data.share_url, + template: template, + target: $(".cartodb-share a"), + size: $(document).width() > 400 ? "" : "small", + width: $(document).width() > 400 ? 430 : 216 + }); + + return dialog.render(); + +}); + +// search content +cdb.vis.Overlay.register('search', function(data, vis) { + + var template = cdb.core.Template.compile( + data.template || '\ +
      \ + \ + \ + \ +
      \ + ', + data.templateType || 'mustache' + ); + + var search = new cdb.geo.ui.Search({ + template: template, + model: vis.map + }); + + return search.render(); +}); + +// tooltip +cdb.vis.Overlay.register('tooltip', function(data, vis) { + var layer; + if (!data.layer) { + var layers = vis.getLayers(); + if(layers.length > 1) { + layer = layers[1]; + } + data.layer = layer; + } + + if(!data.layer) { + throw new Error("layer is null"); + } + data.layer.setInteraction(true); + var tooltip = new cdb.geo.ui.Tooltip(data); + return tooltip; + +}); + +cdb.vis.Overlay.register('infobox', function(data, vis) { + var layer; + var layers = vis.getLayers(); + if (!data.layer) { + if(layers.length > 1) { + layer = layers[1]; + } + data.layer = layer; + } + if(!data.layer) { + throw new Error("layer is null"); + } + data.layer.setInteraction(true); + var infobox = new cdb.geo.ui.InfoBox(data); + return infobox; + +}); + +})(); + +(function() { + +var Layers = cdb.vis.Layers; + +/* + * if we are using http and the tiles of base map need to be fetched from + * https try to fix it + */ + +var HTTPS_TO_HTTP = { + 'https://dnv9my2eseobd.cloudfront.net/': 'http://a.tiles.mapbox.com/', + 'https://maps.nlp.nokia.com/': 'http://maps.nlp.nokia.com/', + 'https://tile.stamen.com/': 'http://tile.stamen.com/', + "https://{s}.maps.nlp.nokia.com/": "http://{s}.maps.nlp.nokia.com/", + "https://cartocdn_{s}.global.ssl.fastly.net/": "http://{s}.api.cartocdn.com/" +}; + +function transformToHTTP(tilesTemplate) { + for(var url in HTTPS_TO_HTTP) { + if(tilesTemplate.indexOf(url) !== -1) { + return tilesTemplate.replace(url, HTTPS_TO_HTTP[url]) + } + } + return tilesTemplate; +} + +Layers.register('tilejson', function(vis, data) { + var url = data.tiles[0]; + url = vis.https ? url: transformToHTTP(url); + return new cdb.geo.TileLayer({ + urlTemplate: url + }); +}); + +Layers.register('tiled', function(vis, data) { + var url = data.urlTemplate; + url = vis.https ? url: transformToHTTP(url); + data.urlTemplate = url; + return new cdb.geo.TileLayer(data); +}); + +Layers.register('wms', function(vis, data) { + return new cdb.geo.WMSLayer(data); +}); + +Layers.register('gmapsbase', function(vis, data) { + return new cdb.geo.GMapsBaseLayer(data); +}); + +Layers.register('plain', function(vis, data) { + return new cdb.geo.PlainLayer(data); +}); + +Layers.register('background', function(vis, data) { + return new cdb.geo.PlainLayer(data); +}); + + +function normalizeOptions(vis, data) { + if(data.infowindow && data.infowindow.fields) { + if(data.interactivity) { + if(data.interactivity.indexOf('cartodb_id') === -1) { + data.interactivity = data.interactivity + ",cartodb_id"; + } + } else { + data.interactivity = 'cartodb_id'; + } + } + // if https is forced + if(vis.https) { + data.tiler_protocol = 'https'; + data.tiler_port = 443; + data.sql_api_protocol = 'https'; + data.sql_api_port = 443; + } + data.cartodb_logo = vis.cartodb_logo == undefined ? data.cartodb_logo : vis.cartodb_logo; +} + +var cartoLayer = function(vis, data) { + normalizeOptions(vis, data); + // if sublayers are included that means a layergroup should + // be created + if(data.sublayers) { + data.type = 'layergroup'; + return new cdb.geo.CartoDBGroupLayer(data); + } + return new cdb.geo.CartoDBLayer(data); +}; + +Layers.register('cartodb', cartoLayer); +Layers.register('carto', cartoLayer); + +Layers.register('layergroup', function(vis, data) { + normalizeOptions(vis, data); + return new cdb.geo.CartoDBGroupLayer(data); +}); + +Layers.register('namedmap', function(vis, data) { + normalizeOptions(vis, data); + return new cdb.geo.CartoDBNamedMapLayer(data); +}); + +Layers.register('torque', function(vis, data) { + // default is https + if(vis.https) { + if(data.sql_api_domain && data.sql_api_domain.indexOf('cartodb.com') !== -1) { + data.sql_api_protocol = 'https'; + data.sql_api_port = 443; + data.tiler_protocol = 'https'; + data.tiler_port = 443; + } + } + data.cartodb_logo = vis.cartodb_logo == undefined ? data.cartodb_logo : vis.cartodb_logo; + return new cdb.geo.TorqueLayer(data); +}); + +})(); +/** + * public api for cartodb + */ + +(function() { + + + function _Promise() { + + } + _.extend(_Promise.prototype, Backbone.Events, { + done: function(fn) { + return this.bind('done', fn); + }, + error: function(fn) { + return this.bind('error', fn); + } + }); + + cdb._Promise = _Promise; + + var _requestCache = {}; + + /** + * compose cartodb url + */ + function cartodbUrl(opts) { + var host = opts.host || 'cartodb.com'; + var protocol = opts.protocol || 'https'; + return protocol + '://' + opts.user + '.' + host + '/api/v1/viz/' + opts.table + '/viz.json'; + } + + /** + * given layer params fetchs the layer json + */ + function _getLayerJson(layer, callback) { + var url = null; + if(layer.layers !== undefined || ((layer.kind || layer.type) !== undefined)) { + // layer object contains the layer data + _.defer(function() { callback(layer); }); + return; + } else if(layer.table !== undefined && layer.user !== undefined) { + // layer object points to cartodbjson + url = cartodbUrl(layer); + } else if(layer.indexOf && layer.indexOf('http') === 0) { + // fetch from url + url = layer; + } + if(url) { + cdb.vis.Loader.get(url, callback); + } else { + _.defer(function() { callback(null); }); + } + } + + /** + * create a layer for the specified map + * + * @param map should be a L.Map or google.maps.Map object + * @param layer should be an url or a javascript object with the data to create the layer + * @param options layer options + * + */ + + cartodb.createLayer = function(map, layer, options, callback) { + + var promise = new _Promise(); + var layerView, MapType; + options = options || {}; + if(map === undefined) { + throw new TypeError("map should be provided"); + } + if(layer === undefined) { + throw new TypeError("layer should be provided"); + } + var args = arguments, + fn = args[args.length -1]; + if(_.isFunction(fn)) { + callback = fn; + } + + promise.addTo = function(map, position) { + promise.on('done', function() { + MapType.addLayerToMap(layerView, map, position); + }); + return promise; + }; + + _getLayerJson(layer, function(visData) { + + var layerData; + + if(!visData) { + promise.trigger('error'); + return; + } + // extract layer data from visualization data + if(visData.layers) { + if(visData.layers.length < 2) { + promise.trigger('error', "visualization file does not contain layer info"); + } + var idx = options.layerIndex === undefined ? 1: options.layerIndex; + if(visData.layers.length <= idx) { + promise.trigger('error', 'layerIndex out of bounds'); + return; + } + layerData = visData.layers[idx]; + } else { + layerData = visData; + } + + if(!layerData) { + promise.trigger('error'); + return; + } + + + // update options + if(options && !_.isFunction(options)) { + layerData.options = layerData.options || {}; + _.extend(layerData.options, options); + } + + options = _.defaults(options, { + infowindow: true, + https: false, + legends: true, + time_slider: true, + tooltip: true + }); + + // check map type + // TODO: improve checking + if(typeof(map.overlayMapTypes) !== "undefined") { + MapType = cdb.geo.GoogleMapsMapView; + // check if leaflet is loaded globally + } else if(map instanceof L.Map || (window.L && map instanceof window.L.Map)) { + MapType = cdb.geo.LeafletMapView; + } else { + promise.trigger('error', "cartodb.js can't guess the map type"); + return promise; + } + + // create a dummy viz + var viz = map.viz; + if(!viz) { + var mapView = new MapType({ + map_object: map, + map: new cdb.geo.Map() + }); + + map.viz = viz = new cdb.vis.Vis({ + mapView: mapView + }); + + viz.updated_at = visData.updated_at; + viz.https = options.https; + } + + function createLayer() { + layerView = viz.createLayer(layerData, { no_base_layer: true }); + if(!layerView) { + promise.trigger('error', "layer not supported"); + return promise; + } + if(options.infowindow) { + viz.addInfowindow(layerView); + } + if(options.tooltip) { + viz.addTooltip(layerView); + } + if(options.legends) { + viz.addLegends([layerData]); + } + if(options.time_slider && layerView.model.get('type') === 'torque') { + viz.addTimeSlider(layerView); + } + + callback && callback(layerView); + promise.trigger('done', layerView); + } + + // load needed modules + if(!viz.checkModules([layerData])) { + viz.loadModules([layerData], function() { + createLayer(); + }); + } else { + createLayer(); + } + + }); + + return promise; + + }; + + +})(); + +;(function() { + + var root = this; + + root.cartodb = root.cartodb || {}; + + function SQL(options) { + if(cartodb === this || window === this) { + return new SQL(options); + } + if(!options.user) { + throw new Error("user should be provided"); + } + var loc = new String(window.location.protocol); + loc = loc.slice(0, loc.length - 1); + if(loc == 'file') { + loc = 'https'; + } + + this.ajax = options.ajax || (typeof(jQuery) !== 'undefined' ? jQuery.ajax: reqwest); + if(!this.ajax) { + throw new Error("jQuery or reqwest should be loaded"); + } + + this.options = _.defaults(options, { + version: 'v2', + protocol: loc, + jsonp: typeof(jQuery) !== 'undefined' ? !jQuery.support.cors: false + }) + } + + SQL.prototype._host = function() { + var opts = this.options; + if(opts && opts.completeDomain) { + return opts.completeDomain + '/api/' + opts.version + '/sql' + } else { + var host = opts.host || 'cartodb.com'; + var protocol = opts.protocol || 'https'; + + return protocol + '://' + opts.user + '.' + host + '/api/' + opts.version + '/sql'; + } + } + + /** + * var sql = new SQL('cartodb_username'); + * sql.execute("select * form {table} where id = {id}", { + * table: 'test', + * id: '1' + * }) + */ + SQL.prototype.execute = function(sql, vars, options, callback) { + var promise = new cartodb._Promise(); + if(!sql) { + throw new TypeError("sql should not be null"); + } + // setup arguments + var args = arguments, + fn = args[args.length -1]; + if(_.isFunction(fn)) { + callback = fn; + } + options = _.defaults(options || {}, this.options); + var params = { + type: 'get', + dataType: 'json', + crossDomain: true + }; + + if(options.jsonp) { + delete params.crossDomain; + params.dataType = 'jsonp'; + } + + if(options.cache) { + params.cache = options.cache; + } + + // Substitute mapnik tokens + // resolution at zoom level 0 + var res = '156543.03515625'; + // full webmercator extent + var ext = 'ST_MakeEnvelope(-20037508.5,-20037508.5,20037508.5,20037508.5,3857)'; + sql = sql.replace('!bbox!', ext) + .replace('!pixel_width!', res) + .replace('!pixel_height!', res); + + // create query + var query = Mustache.render(sql, vars); + var q = 'q=' + encodeURIComponent(query); + + // request params + var reqParams = ['format', 'dp', 'api_key']; + if (options.extra_params) { + reqParams = reqParams.concat(options.extra_params); + } + for(var i in reqParams) { + var r = reqParams[i]; + var v = options[r]; + if(v) { + q += '&' + r + "=" + v; + } + } + + var isGetRequest = options.type ? options.type == 'get' : params.type == 'get'; + // generate url depending on the http method + params.url = this._host() ; + if(isGetRequest) { + params.url += '?' + q + } else { + params.data = q; + } + + // wrap success and error functions + var success = options.success; + var error = options.error; + if(success) delete options.success; + if(error) delete error.success; + + params.error = function(resp) { + var res = resp.responseText || resp.response; + var errors = res && JSON.parse(res); + promise.trigger('error', errors && errors.error, resp) + if(error) error(resp); + } + params.success = function(resp, status, xhr) { + // manage rewest + if(status == undefined) { + status = resp.status; + xhr = resp; + resp = JSON.parse(resp.response); + } + promise.trigger('done', resp, status, xhr); + if(success) success(resp, status, xhr); + if(callback) callback(resp); + } + + // call ajax + delete options.jsonp; + this.ajax(_.extend(params, options)); + return promise; + } + + SQL.prototype.getBounds = function(sql, vars, options, callback) { + var promise = new cartodb._Promise(); + var args = arguments, + fn = args[args.length -1]; + if(_.isFunction(fn)) { + callback = fn; + } + var s = 'SELECT ST_XMin(ST_Extent(the_geom)) as minx,' + + ' ST_YMin(ST_Extent(the_geom)) as miny,'+ + ' ST_XMax(ST_Extent(the_geom)) as maxx,' + + ' ST_YMax(ST_Extent(the_geom)) as maxy' + + ' from ({{{ sql }}}) as subq'; + sql = Mustache.render(sql, vars); + this.execute(s, { sql: sql }, options) + .done(function(result) { + if (result.rows && result.rows.length > 0 && result.rows[0].maxx != null) { + var c = result.rows[0]; + var minlat = -85.0511; + var maxlat = 85.0511; + var minlon = -179; + var maxlon = 179; + + var clamp = function(x, min, max) { + return x < min ? min : x > max ? max : x; + } + + var lon0 = clamp(c.maxx, minlon, maxlon); + var lon1 = clamp(c.minx, minlon, maxlon); + var lat0 = clamp(c.maxy, minlat, maxlat); + var lat1 = clamp(c.miny, minlat, maxlat); + + var bounds = [[lat0, lon0], [lat1, lon1]]; + promise.trigger('done', bounds); + callback && callback(bounds); + } + }) + .error(function(err) { + promise.trigger('error', err); + }) + + return promise; + + } + + /** + * var people_under_10 = sql + * .table('test') + * .columns(['age', 'column2']) + * .filter('age < 10') + * .limit(15) + * .order_by('age') + * + * people_under_10(function(results) { + * }) + */ + + SQL.prototype.table = function(name) { + + var _name = name; + var _filters; + var _columns = []; + var _limit; + var _order; + var _orderDir; + var _sql = this; + + function _table() { + _table.fetch.apply(_table, arguments); + } + + _table.fetch = function(vars) { + vars = vars || {} + var args = arguments, + fn = args[args.length -1]; + if(_.isFunction(fn)) { + callback = fn; + if(args.length === 1) vars = {}; + } + _sql.execute(_table.sql(), vars, callback); + } + + _table.sql = function() { + var s = "select" + if(_columns.length) { + s += ' ' + _columns.join(',') + ' ' + } else { + s += ' * ' + } + + s += "from " + _name; + + if(_filters) { + s += " where " + _filters; + } + if(_limit) { + s += " limit " + _limit; + } + if(_order) { + s += " order by " + _order; + } + if(_orderDir) { + s += ' ' + _orderDir; + } + + return s; + } + + _table.filter = function(f) { + _filters = f; + return _table; + } + + _table.order_by= function(o) { + _order = o; + return _table; + } + _table.asc = function() { + _orderDir = 'asc' + return _table; + } + + _table.desc = function() { + _orderDir = 'desc' + return _table; + } + + _table.columns = function(c) { + _columns = c; + return _table; + } + + _table.limit = function(l) { + _limit = l; + return _table; + } + + return _table; + + } + + /* + * sql.filter(sql.f().distance('< 10km') + */ + /*cartodb.SQL.geoFilter = function() { + var _sql; + function f() {} + + f.distance = function(qty) { + qty.replace('km', '*1000') + _sql += 'st_distance(the_geom) ' + qty + } + f.or = function() { + } + + f.and = function() { + } + return f; + } + */ + + root.cartodb.SQL = SQL; + +})(); +(function() { + + cartodb.createVis = function(el, vizjson, options, callback) { + + if (!el) { + throw new TypeError("a DOM element should be provided"); + } + + var + args = arguments, + fn = args[args.length -1]; + + if (_.isFunction(fn)) { + callback = fn; + } + + el = (typeof el === 'string' ? document.getElementById(el) : el); + + var vis = new cartodb.vis.Vis({ el: el }); + + if (vizjson) { + + vis.load(vizjson, options); + + if (callback) { + vis.done(callback); + } + + } + + return vis; + + }; + +})(); +/** + * tabbed pane. + * if contains n views inside it and shows only one at once + * + * usage: + * + * var pane = new cdb.ui.common.TabPane({ + * el: $("#container") + * }); + * + * pane.addTab('tab1', new OtherView()); + * pane.addTab('tab2', new OtherView2()); + * pane.addTab('tab3', new OtherView3()); + * + * pane.active('tab1'); + * + * pane.bind('tabEnabled', function(tabName, tabView) { + * pane.bind('tabDisabled', function(tabName, tabView) { + * }); + */ +cdb.ui.common.TabPane = cdb.core.View.extend({ + + initialize: function() { + this.tabs = {}; + this.activeTab = null; + this.activePane = null; + }, + + addTab: function(name, view, options) { + options = options || { active: true }; + if(this.tabs[name] !== undefined) { + cdb.log.debug(name + "already added"); + } else { + this.tabs[name] = view.cid; + this.addView(view); + if(options.after !== undefined) { + var e = this.$el.children()[options.after]; + view.$el.insertAfter(e); + } else if(options.prepend) { + this.$el.prepend(view.el); + } else { + this.$el.append(view.el); + } + this.trigger('tabAdded', name, view); + if(options.active) { + this.active(name); + } else { + view.hide(); + } + } + }, + + getPreviousPane: function() { + var tabs = _.toArray(this.tabs); + var panes = _.toArray(this._subviews); + + var i = _.indexOf(tabs, this.activePane.cid) - 1; + if (i < 0) i = panes.length - 1; + + return panes[i]; + }, + + getNextPane: function() { + var tabs = _.toArray(this.tabs); + var panes = _.toArray(this._subviews); + + var i = 1 + _.indexOf(tabs, this.activePane.cid); + if (i > panes.length - 1) i = 0; + + return panes[i]; + }, + + getPane: function(name) { + var vid = this.tabs[name]; + return this._subviews[vid]; + }, + + getActivePane: function() { + return this.activePane; + }, + + size: function() { + return _.size(this.tabs); + }, + + clean: function() { + this.removeTabs(); + cdb.core.View.prototype.clean.call(this) + }, + + removeTab: function(name) { + if (this.tabs[name] !== undefined) { + var vid = this.tabs[name]; + this._subviews[vid].clean(); + delete this.tabs[name]; + + if (this.activeTab == name) { + this.activeTab = null; + } + + if (_.size(this.tabs)) { + this.active(_.keys(this.tabs)[0]); + } + } + }, + + removeTabs: function() { + for(var name in this.tabs) { + var vid = this.tabs[name]; + this._subviews[vid].clean(); + delete this.tabs[name]; + } + this.activeTab = null; + }, + + active: function(name) { + var + self = this, + vid = this.tabs[name]; + + if (vid !== undefined) { + + if (this.activeTab !== name) { + + var v = this._subviews[vid]; + + if (this.activeTab) { + var vid_old = this._subviews[this.tabs[this.activeTab]]; + + vid_old.hide(); + self.trigger('tabDisabled', this.activeTab , vid_old); + self.trigger('tabDisabled:' + this.activeTab, vid_old); + if(vid_old.deactivated) { + vid_old.deactivated(); + } + } + + v.show(); + if(v.activated) { + v.activated(); + } + + this.activeTab = name; + this.activePane = v; + + self.trigger('tabEnabled', name, v); + self.trigger('tabEnabled:' + name, v); + } + + return this.activePane; + } + }, + + render: function() { + return this; + }, + + each: function(fn) { + var self = this; + _.each(this.tabs, function(cid, tab) { + fn(tab, self.getPane(tab)); + }); + } + +}); +/** + * generic dialog + * + * this opens a dialog in the middle of the screen rendering + * a dialog using cdb.templates 'common/dialog' or template_base option. + * + * inherit class should implement render_content (it could return another widget) + * + * usage example: + * + * var MyDialog = cdb.ui.common.Dialog.extend({ + * render_content: function() { + * return "my content"; + * }, + * }) + * var dialog = new MyDialog({ + * title: 'test', + * description: 'long description here', + * template_base: $('#base_template').html(), + * width: 500 + * }); + * + * $('body').append(dialog.render().el); + * dialog.open(); + * + * TODO: implement draggable + * TODO: modal + * TODO: document modal_type + */ + +cdb.ui.common.Dialog = cdb.core.View.extend({ + + tagName: 'div', + className: 'dialog', + + events: { + 'click .ok': '_ok', + 'click .cancel': '_cancel', + 'click .close': '_cancel' + }, + + default_options: { + title: 'title', + description: '', + ok_title: 'Ok', + cancel_title: 'Cancel', + width: 300, + height: 200, + clean_on_hide: false, + enter_to_confirm: false, + template_name: 'common/views/dialog_base', + ok_button_classes: 'button green', + cancel_button_classes: '', + modal_type: '', + modal_class: '', + include_footer: true, + additionalButtons: [] + }, + + initialize: function() { + _.defaults(this.options, this.default_options); + + _.bindAll(this, 'render', '_keydown'); + + // Keydown bindings for the dialog + $(document).bind('keydown', this._keydown); + + // After removing the dialog, cleaning other bindings + this.bind("clean", this._reClean); + + this.template_base = this.options.template_base ? _.template(this.options.template_base) : cdb.templates.getTemplate(this.options.template_name); + }, + + render: function() { + var $el = this.$el; + + $el.html(this.template_base(this.options)); + + $el.find(".modal").css({ + width: this.options.width + //height: this.options.height + //'margin-left': -this.options.width>>1, + //'margin-top': -this.options.height>>1 + }); + + if(this.render_content) { + + this.$('.content').append(this.render_content()); + } + + if(this.options.modal_class) { + this.$el.addClass(this.options.modal_class); + } + + return this; + }, + + + _keydown: function(e) { + // If clicks esc, goodbye! + if (e.keyCode === 27) { + this._cancel(); + // If clicks enter, same as you click on ok button. + } else if (e.keyCode === 13 && this.options.enter_to_confirm) { + this._ok(); + } + }, + + /** + * helper method that renders the dialog and appends it to body + */ + appendToBody: function() { + $('body').append(this.render().el); + return this; + }, + + _ok: function(ev) { + + if(ev) ev.preventDefault(); + + if (this.ok) { + this.ok(this.result); + } + + this.hide(); + + }, + + _cancel: function(ev) { + + if (ev) { + ev.preventDefault(); + ev.stopPropagation(); + } + + if (this.cancel) { + this.cancel(); + } + + this.hide(); + + }, + + hide: function() { + + this.$el.hide(); + + if (this.options.clean_on_hide) { + this.clean(); + } + + }, + + open: function() { + + this.$el.show(); + + }, + + _reClean: function() { + + $(document).unbind('keydown', this._keydown); + + } + +}); +/** + * generic embbed notification, like twitter "new notifications" + * + * it shows slowly the notification with a message and a close button. + * Optionally you can set a timeout to close + * + * usage example: + * + var notification = new cdb.ui.common.Notificaiton({ + el: "#notification_element", + msg: "error!", + timeout: 1000 + }); + notification.show(); + // close it + notification.close(); +*/ + +cdb.ui.common.Notification = cdb.core.View.extend({ + + tagName: 'div', + className: 'dialog', + + events: { + 'click .close': 'hide' + }, + + default_options: { + timeout: 0, + msg: '', + hideMethod: '', + duration: 'normal' + }, + + initialize: function() { + this.closeTimeout = -1; + _.defaults(this.options, this.default_options); + this.template = this.options.template ? _.template(this.options.template) : cdb.templates.getTemplate('common/notification'); + + this.$el.hide(); + }, + + render: function() { + var $el = this.$el; + $el.html(this.template(this.options)); + if(this.render_content) { + this.$('.content').append(this.render_content()); + } + return this; + }, + + hide: function(ev) { + var self = this; + if (ev) + ev.preventDefault(); + clearTimeout(this.closeTimeout); + if(this.options.hideMethod != '' && this.$el.is(":visible") ) { + this.$el[this.options.hideMethod](this.options.duration, 'swing', function() { + self.$el.html(''); + self.trigger('notificationDeleted'); + self.remove(); + }); + } else { + this.$el.hide(); + self.$el.html(''); + self.trigger('notificationDeleted'); + self.remove(); + } + + }, + + open: function(method, options) { + this.render(); + this.$el.show(method, options); + if(this.options.timeout) { + this.closeTimeout = setTimeout(_.bind(this.hide, this), this.options.timeout); + } + } + +}); + +/** + * generic table + * + * this class creates a HTML table based on Table model (see below) and modify it based on model changes + * + * usage example: + * + var table = new Table({ + model: table + }); + + $('body').append(table.render().el); + + * model should be a collection of Rows + + */ + +/** + * represents a table row + */ +cdb.ui.common.Row = cdb.core.Model.extend({ +}); + +cdb.ui.common.TableData = Backbone.Collection.extend({ + model: cdb.ui.common.Row, + fetched: false, + + initialize: function() { + var self = this; + this.bind('reset', function() { + self.fetched = true; + }) + }, + + /** + * get value for row index and columnName + */ + getCell: function(index, columnName) { + var r = this.at(index); + if(!r) { + return null; + } + return r.get(columnName); + }, + + isEmpty: function() { + return this.length === 0; + } + +}); + +/** + * contains information about the table, mainly the schema + */ +cdb.ui.common.TableProperties = cdb.core.Model.extend({ + + columnNames: function() { + return _.map(this.get('schema'), function(c) { + return c[0]; + }); + }, + + columnName: function(idx) { + return this.columnNames()[idx]; + } +}); + +/** + * renders a table row + */ +cdb.ui.common.RowView = cdb.core.View.extend({ + tagName: 'tr', + + initialize: function() { + + this.model.bind('change', this.render, this); + this.model.bind('destroy', this.clean, this); + this.model.bind('remove', this.clean, this); + this.model.bind('change', this.triggerChange, this); + this.model.bind('sync', this.triggerSync, this); + this.model.bind('error', this.triggerError, this); + + this.add_related_model(this.model); + this.order = this.options.order; + }, + + triggerChange: function() { + this.trigger('changeRow'); + }, + + triggerSync: function() { + this.trigger('syncRow'); + }, + + triggerError: function() { + this.trigger('errorRow') + }, + + valueView: function(colName, value) { + return value; + }, + + render: function() { + var self = this; + var row = this.model; + + var tr = ''; + + var tdIndex = 0; + var td; + if(this.options.row_header) { + td = ''; + } else { + td = ''; + } + var v = self.valueView('', ''); + if(v.html) { + v = v[0].outerHTML; + } + td += v; + td += ''; + tdIndex++; + tr += td + + var attrs = this.order || _.keys(row.attributes); + var tds = ''; + var row_attrs = row.attributes; + for(var i = 0, len = attrs.length; i < len; ++i) { + var key = attrs[i]; + var value = row_attrs[key]; + if(value !== undefined) { + var td = ''; + var v = self.valueView(key, value); + if(v.html) { + v = v[0].outerHTML; + } + td += v; + td += ''; + tdIndex++; + tds += td; + } + } + tr += tds; + this.$el.html(tr).attr('id', 'row_' + row.id); + return this; + }, + + getCell: function(x) { + var childNo = x; + if(this.options.row_header) { + ++x; + } + return this.$('td:eq(' + x + ')'); + }, + + getTableView: function() { + return this.tableView; + } + +}); + +/** + * render a table + * this widget needs two data sources + * - the table model which contains information about the table (columns and so on). See TableProperties + * - the model with the data itself (TableData) + */ +cdb.ui.common.Table = cdb.core.View.extend({ + + tagName: 'table', + rowView: cdb.ui.common.RowView, + + events: { + 'click td': '_cellClick', + 'dblclick td': '_cellDblClick' + }, + + default_options: { + }, + + initialize: function() { + var self = this; + _.defaults(this.options, this.default_options); + this.dataModel = this.options.dataModel; + this.rowViews = []; + + // binding + this.setDataSource(this.dataModel); + this.model.bind('change', this.render, this); + this.model.bind('change:dataSource', this.setDataSource, this); + + // assert the rows are removed when table is removed + this.bind('clean', this.clear_rows, this); + + // prepare for cleaning + this.add_related_model(this.dataModel); + this.add_related_model(this.model); + + // we need to use custom signals to make the tableview aware of a row being deleted, + // because when you delete a point from the map view, sometimes it isn't on the dataModel + // collection, so its destroy doesn't bubble throught there. + // Also, the only non-custom way to acknowledge that a row has been correctly deleted from a server is with + // a sync, that doesn't bubble through the table + this.model.bind('removing:row', function() { + self.rowsBeingDeleted = self.rowsBeingDeleted ? self.rowsBeingDeleted +1 : 1; + self.rowDestroying(); + }); + this.model.bind('remove:row', function() { + if(self.rowsBeingDeleted > 0) { + self.rowsBeingDeleted--; + self.rowDestroyed(); + if(self.dataModel.length == 0) { + self.emptyTable(); + } + } + }); + + }, + + headerView: function(column) { + return column[0]; + }, + + setDataSource: function(dm) { + if(this.dataModel) { + this.dataModel.unbind(null, null, this); + } + this.dataModel = dm; + this.dataModel.bind('reset', this._renderRows, this); + this.dataModel.bind('error', this._renderRows, this); + this.dataModel.bind('add', this.addRow, this); + }, + + _renderHeader: function() { + var self = this; + var thead = $(""); + var tr = $(""); + if(this.options.row_header) { + tr.append($("").append(self.headerView(['', 'header']))); + } else { + tr.append($("").append(self.headerView(['', 'header']))); + } + _(this.model.get('schema')).each(function(col) { + tr.append($("").append(self.headerView(col))); + }); + thead.append(tr); + return thead; + }, + + /** + * remove all rows + */ + clear_rows: function() { + this.$('tfoot').remove(); + this.$('tr.noRows').remove(); + + // unbind rows before cleaning them when all are gonna be removed + var rowView = null; + while(rowView = this.rowViews.pop()) { + // this is a hack to avoid all the elements are removed one by one + rowView.unbind(null, null, this); + // each element removes itself from rowViews + rowView.clean(); + } + // clean all the html at the same time + this.rowViews = []; + }, + + /** + * add rows + */ + addRow: function(row, collection, options) { + var self = this; + var tr = new self.rowView({ + model: row, + order: this.model.columnNames(), + row_header: this.options.row_header + }); + tr.tableView = this; + + tr.bind('clean', function() { + var idx = _.indexOf(self.rowViews, tr); + self.rowViews.splice(idx, 1); + // update index + for(var i = idx; i < self.rowViews.length; ++i) { + self.rowViews[i].$el.attr('data-y', i); + } + }, this); + tr.bind('changeRow', this.rowChanged, this); + tr.bind('saved', this.rowSynched, this); + tr.bind('errorRow', this.rowFailed, this); + tr.bind('saving', this.rowSaving, this); + this.retrigger('saving', tr); + + tr.render(); + if(options && options.index !== undefined && options.index != self.rowViews.length) { + + tr.$el.insertBefore(self.rowViews[options.index].$el); + self.rowViews.splice(options.index, 0, tr); + //tr.$el.attr('data-y', options.index); + // change others view data-y attribute + for(var i = options.index; i < self.rowViews.length; ++i) { + self.rowViews[i].$el.attr('data-y', i); + } + } else { + // at the end + tr.$el.attr('data-y', self.rowViews.length); + self.$el.append(tr.el); + self.rowViews.push(tr); + } + + this.trigger('createRow'); + }, + + /** + * Callback executed when a row change + * @method rowChanged + * @abstract + */ + rowChanged: function() {}, + + /** + * Callback executed when a row is sync + * @method rowSynched + * @abstract + */ + rowSynched: function() {}, + + /** + * Callback executed when a row fails to reach the server + * @method rowFailed + * @abstract + */ + rowFailed: function() {}, + + /** + * Callback executed when a row send a POST to the server + * @abstract + */ + rowSaving: function() {}, + + /** + * Callback executed when a row is being destroyed + * @method rowDestroyed + * @abstract + */ + rowDestroying: function() {}, + + /** + * Callback executed when a row gets destroyed + * @method rowDestroyed + * @abstract + */ + rowDestroyed: function() {}, + + /** + * Callback executed when a row gets destroyed and the table data is empty + * @method emptyTable + * @abstract + */ + emptyTable: function() {}, + + /** + * Checks if the table is empty + * @method isEmptyTable + * @returns boolean + */ + isEmptyTable: function() { + return (this.dataModel.length === 0 && this.dataModel.fetched) + }, + + /** + * render only data rows + */ + _renderRows: function() { + this.clear_rows(); + if(! this.isEmptyTable()) { + if(this.dataModel.fetched) { + var self = this; + + this.dataModel.each(function(row) { + self.addRow(row); + }); + } else { + this._renderLoading(); + } + } else { + this._renderEmpty(); + } + + }, + + _renderLoading: function() { + }, + + _renderEmpty: function() { + }, + + /** + * Method for the children to redefine with the table behaviour when it has no rows. + * @method addEmptyTableInfo + * @abstract + */ + addEmptyTableInfo: function() { + // #to be overwrite by descendant classes + }, + + /** + * render table + */ + render: function() { + var self = this; + + // render header + self.$el.html(self._renderHeader()); + + // render data + self._renderRows(); + + return this; + + }, + + /** + * return jquery cell element of cell x,y + */ + getCell: function(x, y) { + if(this.options.row_header) { + ++y; + } + return this.rowViews[y].getCell(x); + }, + + _cellClick: function(e, evtName) { + evtName = evtName || 'cellClick'; + e.preventDefault(); + var cell = $(e.currentTarget || e.target); + var x = parseInt(cell.attr('data-x'), 10); + var y = parseInt(cell.parent().attr('data-y'), 10); + this.trigger(evtName, e, cell, x, y); + }, + + _cellDblClick: function(e) { + this._cellClick(e, 'cellDblClick'); + } + + +}); +(function() { + +/** + * this module implements all the features related to overlay geometries + * in leaflet: markers, polygons, lines and so on + */ + + +/** + * view for markers + */ +function PointView(geometryModel) { + var self = this; + // events to link + var events = [ + 'click', + 'dblclick', + 'mousedown', + 'mouseover', + 'mouseout', + 'dragstart', + 'drag', + 'dragend' + ]; + + this._eventHandlers = {}; + this.model = geometryModel; + this.points = []; + + this.geom = L.GeoJSON.geometryToLayer(geometryModel.get('geojson'), function(geojson, latLng) { + //TODO: create marker depending on the visualizacion options + var p = L.marker(latLng,{ + icon: L.icon({ + iconUrl: cdb.config.get('assets_url') + '/images/layout/default_marker.png', + iconAnchor: [11, 11] + }) + }); + + var i; + for(i = 0; i < events.length; ++i) { + var e = events[i]; + p.on(e, self._eventHandler(e)); + } + return p; + }); + + this.bind('dragend', function(e, pos) { + geometryModel.set({ + geojson: { + type: 'Point', + //geojson is lng,lat + coordinates: [pos[1], pos[0]] + } + }); + }); +} + +PointView.prototype = new GeometryView(); + +PointView.prototype.edit = function() { + this.geom.dragging.enable(); +}; + +/** + * returns a function to handle events fot evtType + */ +PointView.prototype._eventHandler = function(evtType) { + var self = this; + var h = this._eventHandlers[evtType]; + if(!h) { + h = function(e) { + var latlng = e.target.getLatLng(); + var s = [latlng.lat, latlng.lng]; + self.trigger(evtType, e.originalEvent, s); + }; + this._eventHandlers[evtType] = h; + } + return h; +}; + +/** + * view for other geometries (polygons/lines) + */ +function PathView(geometryModel) { + var self = this; + // events to link + var events = [ + 'click', + 'dblclick', + 'mousedown', + 'mouseover', + 'mouseout', + ]; + + this._eventHandlers = {}; + this.model = geometryModel; + this.points = []; + + + this.geom = L.GeoJSON.geometryToLayer(geometryModel.get('geojson')); + this.geom.setStyle(geometryModel.get('style')); + + + /*for(var i = 0; i < events.length; ++i) { + var e = events[i]; + this.geom.on(e, self._eventHandler(e)); + }*/ + +} + +PathView.prototype = new GeometryView(); + +PathView.prototype._leafletLayers = function() { + // check if this is a multi-feature or single-feature + if (this.geom.getLayers) { + return this.geom.getLayers(); + } + return [this.geom]; +}; + + +PathView.prototype.enableEdit = function() { + var self = this; + var layers = this._leafletLayers(); + _.each(layers, function(g) { + g.setStyle(self.model.get('style')); + g.on('edit', function() { + self.model.set('geojson', self.geom.toGeoJSON().geometry); + }, self); + }); +}; + +PathView.prototype.disableEdit = function() { + var self = this; + var layers = this._leafletLayers(); + _.each(layers, function(g) { + g.off('edit', null, self); + }); +}; + +PathView.prototype.edit = function(enable) { + var self = this; + var fn = enable ? 'enable': 'disable'; + var layers = this._leafletLayers(); + _.each(layers, function(g) { + g.editing[fn](); + enable ? self.enableEdit(): self.disableEdit(); + }); +}; + +cdb.geo.leaflet = cdb.geo.leaflet || {}; + +cdb.geo.leaflet.PointView = PointView; +cdb.geo.leaflet.PathView = PathView; + + +})(); +(function() { +/** + * view for markers + */ +function PointView(geometryModel) { + var self = this; + // events to link + var events = [ + 'click', + 'dblclick', + 'mousedown', + 'mouseover', + 'mouseout', + 'dragstart', + 'drag', + 'dragend' + ]; + + this._eventHandlers = {}; + this.model = geometryModel; + this.points = []; + + var style = _.clone(geometryModel.get('style')) || {}; + //style.path = google.maps.SymbolPath.CIRCLE; + //style.scale = style.weight; + //style.strokeColor = "ff0000"; + //style.strokeOpacity = 1; + //style.strokeWeight = 1; + //style.fillColor = '00000'; + //style.fillOpacity = 0.5; + + this.geom = new GeoJSON ( + geometryModel.get('geojson'), + { + icon: { + url: cdb.config.get('assets_url') + '/images/layout/default_marker.png', + anchor: {x: 10, y: 10} + }, + raiseOnDrag: false, + crossOnDrag: false + } + ); + + // bind events + var i; + for(i = 0; i < events.length; ++i) { + var e = events[i]; + google.maps.event.addListener(this.geom, e, self._eventHandler(e)); + } + + // link dragging + this.bind('dragend', function(e, pos) { + geometryModel.set({ + geojson: { + type: 'Point', + // geojson is lng,lat + coordinates: [pos[1], pos[0]] + } + }); + }); +} + +PointView.prototype = new GeometryView(); + +PointView.prototype._eventHandler = function(evtType) { + var self = this; + var h = this._eventHandlers[evtType]; + if(!h) { + h = function(e) { + var latlng = e.latLng; + var s = [latlng.lat(), latlng.lng()]; + self.trigger(evtType, e, s); + }; + this._eventHandlers[evtType] = h; + } + return h; +}; + +PointView.prototype.edit = function(enable) { + this.geom.setDraggable(enable); +}; + +/** + * view for other geometries (polygons/lines) + */ +function PathView(geometryModel) { + var self = this; + // events to link + var events = [ + 'click', + 'dblclick', + 'mousedown', + 'mouseover', + 'mouseout', + ]; + + this._eventHandlers = {}; + this.model = geometryModel; + this.points = []; + + + + var style = _.clone(geometryModel.get('style')) || {}; + + this.geom = new GeoJSON ( + geometryModel.get('geojson'), + style + ); + + /*_.each(this.geom._layers, function(g) { + g.setStyle(geometryModel.get('style')); + g.on('edit', function() { + geometryModel.set('geojson', L.GeoJSON.toGeoJSON(self.geom)); + }, self); + }); + */ + + _.bindAll(this, '_updateModel'); + var self = this; + + function bindPath(p) { + google.maps.event.addListener(p, 'insert_at', self._updateModel); + /* + google.maps.event.addListener(p, 'remove_at', this._updateModel); + google.maps.event.addListener(p, 'set_at', this._updateModel); + */ + } + + // TODO: check this conditions + + if(this.geom.getPaths) { + var paths = this.geom.getPaths(); + + if (paths && paths[0]) { + // More than one path + for(var i = 0; i < paths.length; ++i) { + bindPath(paths[i]); + } + } else { + // One path + bindPath(paths); + google.maps.event.addListener(this.geom, 'mouseup', this._updateModel); + } + } else { + // More than one path + if (this.geom.length) { + for(var i = 0; i < this.geom.length; ++i) { + bindPath(this.geom[i].getPath()); + google.maps.event.addListener(this.geom[i], 'mouseup', this._updateModel); + } + } else { + // One path + bindPath(this.geom.getPath()); + google.maps.event.addListener(this.geom, 'mouseup', this._updateModel); + } + } + + /*for(var i = 0; i < events.length; ++i) { + var e = events[i]; + this.geom.on(e, self._eventHandler(e)); + }*/ + +} + +PathView.prototype = new GeometryView(); + +PathView.getGeoJSON = function(geom, gType) { + + var coordFn = { + 'Polygon': 'getPath', + 'MultiPolygon': 'getPath', + 'LineString': 'getPath', + 'MultiLineString': 'getPath', + 'Point': 'getPosition', + 'MultiPoint': 'getPosition' + }; + + function _coord(latlng) { + return [latlng.lng(), latlng.lat()]; + } + + function _coords(latlngs) { + var c = []; + for(var i = 0; i < latlngs.length; ++i) { + c.push(_coord(latlngs.getAt(i))); + } + return c; + } + + // single + if(!geom.length || geom.length == 1) { + var g = geom.length ? geom[0]: geom; + var coords; + if(gType == 'Point') { + coords = _coord(g.getPosition()); + } else if(gType == 'MultiPoint') { + coords = [_coord(g.getPosition())] + } else if(gType == 'Polygon') { + coords = [_coords(g.getPath())]; + coords[0].push(_coord(g.getPath().getAt(0))); + } else if(gType == 'MultiPolygon') { + coords = []; + for(var p = 0; p < g.getPaths().length; ++p) { + var c = _coords(g.getPaths().getAt(p)); + c.push(_coord(g.getPaths().getAt(p).getAt(0))); + coords.push(c); + } + coords = [coords] + } else if(gType == 'LineString') { + coords = _coords(g.getPath()); + } else if(gType == 'MultiLineString') { + //TODO: redo + coords = [_coords(g.getPath())]; + } + return { + type: gType, + coordinates: coords + } + } else { + // poly + var c = []; + for(var i = 0; i < geom.length; ++i) { + c.push(PathView.getGeoJSON(geom[i], gType).coordinates[0]); + } + return { + type: gType, + coordinates: c + } + } +} + +PathView.prototype._updateModel = function(e) { + var self = this; + setTimeout(function() { + self.model.set('geojson', PathView.getGeoJSON(self.geom, self.model.get('geojson').type )); + }, 100) +} + +PathView.prototype.edit = function(enable) { + + var fn = enable ? 'enable': 'disable'; + var g = this.geom.length ? this.geom: [this.geom]; + for(var i = 0; i < g.length; ++i) { + g[i].setEditable(enable); + } + if(!enable) { + this.model.set('geojson', PathView.getGeoJSON(this.geom, this.model.get('geojson').type)); + } +}; + +cdb.geo.gmaps = cdb.geo.gmaps || {}; + +cdb.geo.gmaps.PointView = PointView; +cdb.geo.gmaps.PathView = PathView; + + + +})(); + + + cdb.$ = $; + cdb.L = L; + cdb.Mustache = Mustache; + cdb.Backbone = Backbone; + cdb._ = _; + + })(); + + + + + ; + for(var i in __prev) { + // keep it at global context if it didn't exist + if(__prev[i]) { + window[i] = __prev[i]; + } + } + + +})(); diff --git a/vendor/assets/bower_components/cartodb.js/dist/cartodb.ie.css b/vendor/assets/bower_components/cartodb.js/dist/cartodb.ie.css new file mode 100644 index 0000000000..61c5237260 --- /dev/null +++ b/vendor/assets/bower_components/cartodb.js/dist/cartodb.ie.css @@ -0,0 +1 @@ +/** you don't need to load this file anymore */ diff --git a/vendor/assets/bower_components/cartodb.js/dist/cartodb.js b/vendor/assets/bower_components/cartodb.js/dist/cartodb.js new file mode 100644 index 0000000000..3c027cba11 --- /dev/null +++ b/vendor/assets/bower_components/cartodb.js/dist/cartodb.js @@ -0,0 +1,20 @@ +// cartodb.js version: 3.10.1 +// uncompressed version: cartodb.uncompressed.js +// sha: 270a2bc86c4cbf9e8b6256fa6e79371cc58bce4d +(function(){var root=this,__prev={jQuery:root.jQuery,$:root.$,L:root.L,Mustache:root.Mustache,Backbone:root.Backbone,_:root._};(function(e,t){function n(e){return H.isWindow(e)?e:e.nodeType===9?e.defaultView||e.parentWindow:!1}function r(e){if(!vn[e]){var t=_.body,n=H("<"+e+">").appendTo(t),r=n.css("display");n.remove();if(r==="none"||r===""){mn||(mn=_.createElement("iframe"),mn.frameBorder=mn.width=mn.height=0),t.appendChild(mn);if(!gn||!mn.createElement)gn=(mn.contentWindow||mn.contentDocument).document,gn.write((H.support.boxModel?"":"")+""),gn.close();n=gn.createElement(e),gn.body.appendChild(n),r=H.css(n,"display"),t.removeChild(mn)}vn[e]=r}return vn[e]}function i(e,t){var n={};return H.each(En.concat.apply([],En.slice(0,t)),function(){n[this]=e}),n}function s(){Sn=t}function o(){return setTimeout(s,0),Sn=H.now()}function u(){try{return new e.ActiveXObject("Microsoft.XMLHTTP")}catch(t){}}function a(){try{return new e.XMLHttpRequest}catch(t){}}function f(e,n){e.dataFilter&&(n=e.dataFilter(n,e.dataType));var r=e.dataTypes,i={},s,o,u=r.length,a,f=r[0],l,c,h,p,d;for(s=1;s0){if(n!=="border")for(;i=0===n})}function N(e){return!e||!e.parentNode||e.parentNode.nodeType===11}function C(){return!0}function k(){return!1}function L(e,t,n){var r=t+"defer",i=t+"queue",s=t+"mark",o=H._data(e,r);o&&(n==="queue"||!H._data(e,i))&&(n==="mark"||!H._data(e,s))&&setTimeout(function(){!H._data(e,i)&&!H._data(e,s)&&(H.removeData(e,r,!0),o.fire())},0)}function A(e){for(var t in e){if(t==="data"&&H.isEmptyObject(e[t]))continue;if(t!=="toJSON")return!1}return!0}function O(e,n,r){if(r===t&&e.nodeType===1){var i="data-"+n.replace(I,"-$1").toLowerCase();r=e.getAttribute(i);if(typeof r=="string"){try{r=r==="true"?!0:r==="false"?!1:r==="null"?null:H.isNumeric(r)?+r:F.test(r)?H.parseJSON(r):r}catch(s){}H.data(e,n,r)}else r=t}return r}function M(e){var t=B[e]={},n,r;e=e.split(/\s+/);for(n=0,r=e.length;n)[^>]*$|#([\w\-]*)$)/,a=/\S/,f=/^\s+/,l=/\s+$/,c=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,h=/^[\],:{}\s]*$/,p=/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,d=/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,v=/(?:^|:|,)(?:\s*\[)+/g,m=/(webkit)[ \/]([\w.]+)/,g=/(opera)(?:.*version)?[ \/]([\w.]+)/,y=/(msie) ([\w.]+)/,b=/(mozilla)(?:.*? rv:([\w.]+))?/,w=/-([a-z]|[0-9])/ig,E=/^-ms-/,S=function(e,t){return(t+"").toUpperCase()},x=D.userAgent,T,N,C,k=Object.prototype.toString,L=Object.prototype.hasOwnProperty,A=Array.prototype.push,O=Array.prototype.slice,M=String.prototype.trim,P=Array.prototype.indexOf,H={};return r.fn=r.prototype={constructor:r,init:function(e,n,i){var s,o,a,f;if(!e)return this;if(e.nodeType)return this.context=this[0]=e,this.length=1,this;if(e==="body"&&!n&&_.body)return this.context=_,this[0]=_.body,this.selector=e,this.length=1,this;if(typeof e=="string"){e.charAt(0)!=="<"||e.charAt(e.length-1)!==">"||e.length<3?s=u.exec(e):s=[null,e,null];if(s&&(s[1]||!n)){if(s[1])return n=n instanceof r?n[0]:n,f=n?n.ownerDocument||n:_,a=c.exec(e),a?r.isPlainObject(n)?(e=[_.createElement(a[1])],r.fn.attr.call(e,n,!0)):e=[f.createElement(a[1])]:(a=r.buildFragment([s[1]],[f]),e=(a.cacheable?r.clone(a.fragment):a.fragment).childNodes),r.merge(this,e);o=_.getElementById(s[2]);if(o&&o.parentNode){if(o.id!==s[2])return i.find(e);this.length=1,this[0]=o}return this.context=_,this.selector=e,this}return!n||n.jquery?(n||i).find(e):this.constructor(n).find(e)}return r.isFunction(e)?i.ready(e):(e.selector!==t&&(this.selector=e.selector,this.context=e.context),r.makeArray(e,this))},selector:"",jquery:"1.7.2",length:0,size:function(){return this.length},toArray:function(){return O.call(this,0)},get:function(e){return e==null?this.toArray():e<0?this[this.length+e]:this[e]},pushStack:function(e,t,n){var i=this.constructor();return r.isArray(e)?A.apply(i,e):r.merge(i,e),i.prevObject=this,i.context=this.context,t==="find"?i.selector=this.selector+(this.selector?" ":"")+n:t&&(i.selector=this.selector+"."+t+"("+n+")"),i},each:function(e,t){return r.each(this,e,t)},ready:function(e){return r.bindReady(),N.add(e),this},eq:function(e){return e=+e,e===-1?this.slice(e):this.slice(e,e+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(O.apply(this,arguments),"slice",O.call(arguments).join(","))},map:function(e){return this.pushStack(r.map(this,function(t,n){return e.call(t,n,t)}))},end:function(){return this.prevObject||this.constructor(null)},push:A,sort:[].sort,splice:[].splice},r.fn.init.prototype=r.fn,r.extend=r.fn.extend=function(){var e,n,i,s,o,u,a=arguments[0]||{},f=1,l=arguments.length,c=!1;typeof a=="boolean"&&(c=a,a=arguments[1]||{},f=2),typeof a!="object"&&!r.isFunction(a)&&(a={}),l===f&&(a=this,--f);for(;f0)return;N.fireWith(_,[r]),r.fn.trigger&&r(_).trigger("ready").off("ready")}},bindReady:function(){if(!N){N=r.Callbacks("once memory");if(_.readyState==="complete")return setTimeout(r.ready,1);if(_.addEventListener)_.addEventListener("DOMContentLoaded",C,!1),e.addEventListener("load",r.ready,!1);else if(_.attachEvent){_.attachEvent("onreadystatechange",C),e.attachEvent("onload",r.ready);var t=!1;try{t=e.frameElement==null}catch(i){}_.documentElement.doScroll&&t&&n()}}},isFunction:function(e){return r.type(e)==="function"},isArray:Array.isArray||function(e){return r.type(e)==="array"},isWindow:function(e){return e!=null&&e==e.window},isNumeric:function(e){return!isNaN(parseFloat(e))&&isFinite(e)},type:function(e){return e==null?String(e):H[k.call(e)]||"object"},isPlainObject:function(e){if(!e||r.type(e)!=="object"||e.nodeType||r.isWindow(e))return!1;try{if(e.constructor&&!L.call(e,"constructor")&&!L.call(e.constructor.prototype,"isPrototypeOf"))return!1}catch(n){return!1}var i;for(i in e);return i===t||L.call(e,i)},isEmptyObject:function(e){for(var t in e)return!1;return!0},error:function(e){throw new Error(e)},parseJSON:function(t){if(typeof t!="string"||!t)return null;t=r.trim(t);if(e.JSON&&e.JSON.parse)return e.JSON.parse(t);if(h.test(t.replace(p,"@").replace(d,"]").replace(v,"")))return(new Function("return "+t))();r.error("Invalid JSON: "+t)},parseXML:function(n){if(typeof n!="string"||!n)return null;var i,s;try{e.DOMParser?(s=new DOMParser,i=s.parseFromString(n,"text/xml")):(i=new ActiveXObject("Microsoft.XMLDOM"),i.async="false",i.loadXML(n))}catch(o){i=t}return(!i||!i.documentElement||i.getElementsByTagName("parsererror").length)&&r.error("Invalid XML: "+n),i},noop:function(){},globalEval:function(t){t&&a.test(t)&&(e.execScript||function(t){e.eval.call(e,t)})(t)},camelCase:function(e){return e.replace(E,"ms-").replace(w,S)},nodeName:function(e,t){return e.nodeName&&e.nodeName.toUpperCase()===t.toUpperCase()},each:function(e,n,i){var s,o=0,u=e.length,a=u===t||r.isFunction(e);if(i){if(a){for(s in e)if(n.apply(e[s],i)===!1)break}else for(;o0&&e[0]&&e[f-1]||f===0||r.isArray(e));if(l)for(;a1?j.call(arguments,0):t,f.notifyWith(l,o)}}function n(e){return function(t){r[e]=arguments.length>1?j.call(arguments,0):t,--u||f.resolveWith(f,r)}}var r=j.call(arguments,0),i=0,s=r.length,o=Array(s),u=s,a=s,f=s<=1&&e&&H.isFunction(e.promise)?e:H.Deferred(),l=f.promise();if(s>1){for(;i
      a",n=p.getElementsByTagName("*"),r=p.getElementsByTagName("a")[0];if(!n||!n.length||!r)return{};i=_.createElement("select"),s=i.appendChild(_.createElement("option")),o=p.getElementsByTagName("input")[0],t={leadingWhitespace:p.firstChild.nodeType===3,tbody:!p.getElementsByTagName("tbody").length,htmlSerialize:!!p.getElementsByTagName("link").length,style:/top/.test(r.getAttribute("style")),hrefNormalized:r.getAttribute("href")==="/a",opacity:/^0.55/.test(r.style.opacity),cssFloat:!!r.style.cssFloat,checkOn:o.value==="on",optSelected:s.selected,getSetAttribute:p.className!=="t",enctype:!!_.createElement("form").enctype,html5Clone:_.createElement("nav").cloneNode(!0).outerHTML!=="<:nav>",submitBubbles:!0,changeBubbles:!0,focusinBubbles:!1,deleteExpando:!0,noCloneEvent:!0,inlineBlockNeedsLayout:!1,shrinkWrapBlocks:!1,reliableMarginRight:!0,pixelMargin:!0},H.boxModel=t.boxModel=_.compatMode==="CSS1Compat",o.checked=!0,t.noCloneChecked=o.cloneNode(!0).checked,i.disabled=!0,t.optDisabled=!s.disabled;try{delete p.test}catch(v){t.deleteExpando=!1}!p.addEventListener&&p.attachEvent&&p.fireEvent&&(p.attachEvent("onclick",function(){t.noCloneEvent=!1}),p.cloneNode(!0).fireEvent("onclick")),o=_.createElement("input"),o.value="t",o.setAttribute("type","radio"),t.radioValue=o.value==="t",o.setAttribute("checked","checked"),o.setAttribute("name","t"),p.appendChild(o),u=_.createDocumentFragment(),u.appendChild(p.lastChild),t.checkClone=u.cloneNode(!0).cloneNode(!0).lastChild.checked,t.appendChecked=o.checked,u.removeChild(o),u.appendChild(p);if(p.attachEvent)for(c in{submit:1,change:1,focusin:1})l="on"+c,h=l in p,h||(p.setAttribute(l,"return;"),h=typeof p[l]=="function"),t[c+"Bubbles"]=h;return u.removeChild(p),u=i=s=p=o=null,H(function(){var n,r,i,s,o,u,f,l,c,d,v,m,g,y=_.getElementsByTagName("body")[0];!y||(l=1,g="padding:0;margin:0;border:",v="position:absolute;top:0;left:0;width:1px;height:1px;",m=g+"0;visibility:hidden;",c="style='"+v+g+"5px solid #000;",d="
      "+""+"
      ",n=_.createElement("div"),n.style.cssText=m+"width:0;height:0;position:static;top:0;margin-top:"+l+"px",y.insertBefore(n,y.firstChild),p=_.createElement("div"),n.appendChild(p),p.innerHTML="
      t
      ",a=p.getElementsByTagName("td"),h=a[0].offsetHeight===0,a[0].style.display="",a[1].style.display="none",t.reliableHiddenOffsets=h&&a[0].offsetHeight===0,e.getComputedStyle&&(p.innerHTML="",f=_.createElement("div"),f.style.width="0",f.style.marginRight="0",p.style.width="2px",p.appendChild(f),t.reliableMarginRight=(parseInt((e.getComputedStyle(f,null)||{marginRight:0}).marginRight,10)||0)===0),typeof p.style.zoom!="undefined"&&(p.innerHTML="",p.style.width=p.style.padding="1px",p.style.border=0,p.style.overflow="hidden",p.style.display="inline",p.style.zoom=1,t.inlineBlockNeedsLayout=p.offsetWidth===3,p.style.display="block",p.style.overflow="visible",p.innerHTML="
      ",t.shrinkWrapBlocks=p.offsetWidth!==3),p.style.cssText=v+m,p.innerHTML=d,r=p.firstChild,i=r.firstChild,o=r.nextSibling.firstChild.firstChild,u={doesNotAddBorder:i.offsetTop!==5,doesAddBorderForTableAndCells:o.offsetTop===5},i.style.position="fixed",i.style.top="20px",u.fixedPosition=i.offsetTop===20||i.offsetTop===15,i.style.position=i.style.top="",r.style.overflow="hidden",r.style.position="relative",u.subtractsBorderForOverflowNotVisible=i.offsetTop===-5,u.doesNotIncludeMarginInBodyOffset=y.offsetTop!==l,e.getComputedStyle&&(p.style.marginTop="1%",t.pixelMargin=(e.getComputedStyle(p,null)||{marginTop:0}).marginTop!=="1%"),typeof n.style.zoom!="undefined"&&(n.style.zoom=1),y.removeChild(n),f=p=n=null,H.extend(t,u))}),t}();var F=/^(?:\{.*\}|\[.*\])$/,I=/([A-Z])/g;H.extend({cache:{},uuid:0,expando:"jQuery"+(H.fn.jquery+Math.random()).replace(/\D/g,""),noData:{embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:!0},hasData:function(e){return e=e.nodeType?H.cache[e[H.expando]]:e[H.expando],!!e&&!A(e)},data:function(e,n,r,i){if(!!H.acceptData(e)){var s,o,u,a=H.expando,f=typeof n=="string",l=e.nodeType,c=l?H.cache:e,h=l?e[a]:e[a]&&a,p=n==="events";if((!h||!c[h]||!p&&!i&&!c[h].data)&&f&&r===t)return;h||(l?e[a]=h=++H.uuid:h=a),c[h]||(c[h]={},l||(c[h].toJSON=H.noop));if(typeof n=="object"||typeof n=="function")i?c[h]=H.extend(c[h],n):c[h].data=H.extend(c[h].data,n);return s=o=c[h],i||(o.data||(o.data={}),o=o.data),r!==t&&(o[H.camelCase(n)]=r),p&&!o[n]?s.events:(f?(u=o[n],u==null&&(u=o[H.camelCase(n)])):u=o,u)}},removeData:function(e,t,n){if(!!H.acceptData(e)){var r,i,s,o=H.expando,u=e.nodeType,a=u?H.cache:e,f=u?e[o]:o;if(!a[f])return;if(t){r=n?a[f]:a[f].data;if(r){H.isArray(t)||(t in r?t=[t]:(t=H.camelCase(t),t in r?t=[t]:t=t.split(" ")));for(i=0,s=t.length;i1,null,!1))},removeData:function(e){return this.each(function(){H.removeData(this,e)})}}),H.extend({_mark:function(e,t){e&&(t=(t||"fx")+"mark",H._data(e,t,(H._data(e,t)||0)+1))},_unmark:function(e,t,n){e!==!0&&(n=t,t=e,e=!1);if(t){n=n||"fx";var r=n+"mark",i=e?0:(H._data(t,r)||1)-1;i?H._data(t,r,i):(H.removeData(t,r,!0),L(t,n,"mark"))}},queue:function(e,t,n){var r;if(e)return t=(t||"fx")+"queue",r=H._data(e,t),n&&(!r||H.isArray(n)?r=H._data(e,t,H.makeArray(n)):r.push(n)),r||[]},dequeue:function(e,t){t=t||"fx";var n=H.queue(e,t),r=n.shift(),i={};r==="inprogress"&&(r=n.shift()),r&&(t==="fx"&&n.unshift("inprogress"),H._data(e,t+".run",i),r.call(e,function(){H.dequeue(e,t)},i)),n.length||(H.removeData(e,t+"queue "+t+".run",!0),L(e,t,"queue"))}}),H.fn.extend({queue:function(e,n){var r=2;return typeof e!="string"&&(n=e,e="fx",r--),arguments.length1)},removeAttr:function(e){return this.each(function(){H.removeAttr(this,e)})},prop:function(e,t){return H.access(this,H.prop,e,t,arguments.length>1)},removeProp:function(e){return e=H.propFix[e]||e,this.each(function(){try{this[e]=t,delete this[e]}catch(n){}})},addClass:function(e){var t,n,r,i,s,o,u;if(H.isFunction(e))return this.each(function(t){H(this).addClass(e.call(this,t,this.className))});if(e&&typeof e=="string"){t=e.split(R);for(n=0,r=this.length;n-1)return!0;return!1},val:function(e){var n,r,i,s=this[0];if(!!arguments.length)return i=H.isFunction(e),this.each(function(r){var s=H(this),o;if(this.nodeType===1){i?o=e.call(this,r,s.val()):o=e,o==null?o="":typeof o=="number"?o+="":H.isArray(o)&&(o=H.map(o,function(e){return e==null?"":e+""})),n=H.valHooks[this.type]||H.valHooks[this.nodeName.toLowerCase()];if(!n||!("set"in n)||n.set(this,o,"value")===t)this.value=o}});if(s)return n=H.valHooks[s.type]||H.valHooks[s.nodeName.toLowerCase()],n&&"get"in n&&(r=n.get(s,"value"))!==t?r:(r=s.value,typeof r=="string"?r.replace(U,""):r==null?"":r)}}),H.extend({valHooks:{option:{get:function(e){var t=e.attributes.value;return!t||t.specified?e.value:e.text}},select:{get:function(e){var t,n,r,i,s=e.selectedIndex,o=[],u=e.options,a=e.type==="select-one";if(s<0)return null;n=a?s:0,r=a?s+1:u.length;for(;n=0}),n.length||(e.selectedIndex=-1),n}}},attrFn:{val:!0,css:!0,html:!0,text:!0,data:!0,width:!0,height:!0,offset:!0},attr:function(e,n,r,i){var s,o,u,a=e.nodeType;if(!!e&&a!==3&&a!==8&&a!==2){if(i&&n in H.attrFn)return H(e)[n](r);if(typeof e.getAttribute=="undefined")return H.prop(e,n,r);u=a!==1||!H.isXMLDoc(e),u&&(n=n.toLowerCase(),o=H.attrHooks[n]||(V.test(n)?K:J));if(r!==t){if(r===null){H.removeAttr(e,n);return}return o&&"set"in o&&u&&(s=o.set(e,r,n))!==t?s:(e.setAttribute(n,""+r),r)}return o&&"get"in o&&u&&(s=o.get(e,n))!==null?s:(s=e.getAttribute(n),s===null?t:s)}},removeAttr:function(e,t){var n,r,i,s,o,u=0;if(t&&e.nodeType===1){r=t.toLowerCase().split(R),s=r.length;for(;u=0}})});var G=/^(?:textarea|input|select)$/i,Y=/^([^\.]*)?(?:\.(.+))?$/,Z=/(?:^|\s)hover(\.\S+)?\b/,et=/^key/,tt=/^(?:mouse|contextmenu)|click/,nt=/^(?:focusinfocus|focusoutblur)$/,rt=/^(\w*)(?:#([\w\-]+))?(?:\.([\w\-]+))?$/,it=function(e){var t=rt.exec(e);return t&&(t[1]=(t[1]||"").toLowerCase(),t[3]=t[3]&&new RegExp("(?:^|\\s)"+t[3]+"(?:\\s|$)")),t},st=function(e,t){var n=e.attributes||{};return(!t[1]||e.nodeName.toLowerCase()===t[1])&&(!t[2]||(n.id||{}).value=== +t[2])&&(!t[3]||t[3].test((n["class"]||{}).value))},ot=function(e){return H.event.special.hover?e:e.replace(Z,"mouseenter$1 mouseleave$1")};H.event={add:function(e,n,r,i,s){var o,u,a,f,l,c,h,p,d,v,m,g;if(!(e.nodeType===3||e.nodeType===8||!n||!r||!(o=H._data(e)))){r.handler&&(d=r,r=d.handler,s=d.selector),r.guid||(r.guid=H.guid++),a=o.events,a||(o.events=a={}),u=o.handle,u||(o.handle=u=function(e){return typeof H=="undefined"||!!e&&H.event.triggered===e.type?t:H.event.dispatch.apply(u.elem,arguments)},u.elem=e),n=H.trim(ot(n)).split(" ");for(f=0;f=0&&(o=o.slice(0,-1),f=!0),o.indexOf(".")>=0&&(u=o.split("."),o=u.shift(),u.sort());if((!i||H.event.customEvent[o])&&!H.event.global[o])return;n=typeof n=="object"?n[H.expando]?n:new H.Event(o,n):new H.Event(o),n.type=o,n.isTrigger=!0,n.exclusive=f,n.namespace=u.join("."),n.namespace_re=n.namespace?new RegExp("(^|\\.)"+u.join("\\.(?:.*\\.)?")+"(\\.|$)"):null,p=o.indexOf(":")<0?"on"+o:"";if(!i){a=H.cache;for(l in a)a[l].events&&a[l].events[o]&&H.event.trigger(n,r,a[l].handle.elem,!0);return}n.result=t,n.target||(n.target=i),r=r!=null?H.makeArray(r):[],r.unshift(n),d=H.event.special[o]||{};if(d.trigger&&d.trigger.apply(i,r)===!1)return;m=[[i,d.bindType||o]];if(!s&&!d.noBubble&&!H.isWindow(i)){g=d.delegateType||o,c=nt.test(g+o)?i:i.parentNode,h=null;for(;c;c=c.parentNode)m.push([c,g]),h=c;h&&h===i.ownerDocument&&m.push([h.defaultView||h.parentWindow||e,g])}for(l=0;li&&a.push({elem:this,matches:r.slice(i)});for(f=0;f0?this.on(t,null,e,n):this.trigger(t)},H.attrFn&&(H.attrFn[t]=!0),et.test(t)&&(H.event.fixHooks[t]=H.event.keyHooks),tt.test(t)&&(H.event.fixHooks[t]=H.event.mouseHooks)}),function(){function e(e,t,n,r,s,o){for(var u=0,a=r.length;u0){l=f;break}}f=f[e]}r[u]=l}}}function n(e,t,n,r,s,o){for(var u=0,a=r.length;u+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,i="sizcache"+(Math.random()+"").replace(".",""),s=0,o=Object.prototype.toString,u=!1,a=!0,f=/\\/g,l=/\r\n/g,c=/\W/;[0,0].sort(function(){return a=!1,0});var h=function(e,t,n,i){n=n||[],t=t||_;var s=t;if(t.nodeType!==1&&t.nodeType!==9)return[];if(!e||typeof e!="string")return n;var u,a,f,l,c,p,m,g,b=!0,w=h.isXML(t),E=[],x=e;do{r.exec(""),u=r.exec(x);if(u){x=u[3],E.push(u[1]);if(u[2]){l=u[3];break}}}while(u);if(E.length>1&&v.exec(e))if(E.length===2&&d.relative[E[0]])a=S(E[0]+E[1],t,i);else{a=d.relative[E[0]]?[t]:h(E.shift(),t);while(E.length)e=E.shift(),d.relative[e]&&(e+=E.shift()),a=S(e,a,i)}else{!i&&E.length>1&&t.nodeType===9&&!w&&d.match.ID.test(E[0])&&!d.match.ID.test(E[E.length-1])&&(c=h.find(E.shift(),t,w),t=c.expr?h.filter(c.expr,c.set)[0]:c.set[0]);if(t){c=i?{expr:E.pop(),set:y(i)}:h.find(E.pop(),E.length!==1||E[0]!=="~"&&E[0]!=="+"||!t.parentNode?t:t.parentNode,w),a=c.expr?h.filter(c.expr,c.set):c.set,E.length>0?f=y(a):b=!1;while(E.length)p=E.pop(),m=p,d.relative[p]?m=E.pop():p="",m==null&&(m=t),d.relative[p](f,m,w)}else f=E=[]}f||(f=a),f||h.error(p||e);if(o.call(f)==="[object Array]")if(!b)n.push.apply(n,f);else if(t&&t.nodeType===1)for(g=0;f[g]!=null;g++)f[g]&&(f[g]===!0||f[g].nodeType===1&&h.contains(t,f[g]))&&n.push(a[g]);else for(g=0;f[g]!=null;g++)f[g]&&f[g].nodeType===1&&n.push(a[g]);else y(f,n);return l&&(h(l,s,n,i),h.uniqueSort(n)),n};h.uniqueSort=function(e){if(w){u=a,e.sort(w);if(u)for(var t=1;t0},h.find=function(e,t,n){var r,i,s,o,u,a;if(!e)return[];for(i=0,s=d.order.length;i":function(e,t){var n,r=typeof t=="string",i=0,s=e.length;if(r&&!c.test(t)){t=t.toLowerCase();for(;i=0)?n||r.push(u):n&&(t[o]=!1));return!1},ID:function(e){return e[1].replace(f,"")},TAG:function(e,t){return e[1].replace(f,"").toLowerCase()},CHILD:function(e){if(e[1]==="nth"){e[2]||h.error(e[0]),e[2]=e[2].replace(/^\+|\s*/g,"");var t=/(-?)(\d*)(?:n([+\-]?\d*))?/.exec(e[2]==="even"&&"2n"||e[2]==="odd"&&"2n+1"||!/\D/.test(e[2])&&"0n+"+e[2]||e[2]);e[2]=t[1]+(t[2]||1)-0,e[3]=t[3]-0}else e[2]&&h.error(e[0]);return e[0]=s++,e},ATTR:function(e,t,n,r,i,s){var o=e[1]=e[1].replace(f,"");return!s&&d.attrMap[o]&&(e[1]=d.attrMap[o]),e[4]=(e[4]||e[5]||"").replace(f,""),e[2]==="~="&&(e[4]=" "+e[4]+" "),e},PSEUDO:function(e,t,n,i,s){if(e[1]==="not"){if(!((r.exec(e[3])||"").length>1||/^\w/.test(e[3]))){var o=h.filter(e[3],t,n,!0^s);return n||i.push.apply(i,o),!1}e[3]=h(e[3],null,null,t)}else if(d.match.POS.test(e[0])||d.match.CHILD.test(e[0]))return!0;return e},POS:function(e){return e.unshift(!0),e}},filters:{enabled:function(e){return e.disabled===!1&&e.type!=="hidden"},disabled:function(e){return e.disabled===!0},checked:function(e){return e.checked===!0},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,e.selected===!0},parent:function(e){return!!e.firstChild},empty:function(e){return!e.firstChild},has:function(e,t,n){return!!h(n[3],e).length},header:function(e){return/h\d/i.test(e.nodeName)},text:function(e){var t=e.getAttribute("type"),n=e.type;return e.nodeName.toLowerCase()==="input"&&"text"===n&&(t===n||t===null)},radio:function(e){return e.nodeName.toLowerCase()==="input"&&"radio"===e.type},checkbox:function(e){return e.nodeName.toLowerCase()==="input"&&"checkbox"===e.type},file:function(e){return e.nodeName.toLowerCase()==="input"&&"file"===e.type},password:function(e){return e.nodeName.toLowerCase()==="input"&&"password"===e.type},submit:function(e){var t=e.nodeName.toLowerCase();return(t==="input"||t==="button")&&"submit"===e.type},image:function(e){return e.nodeName.toLowerCase()==="input"&&"image"===e.type},reset:function(e){var t=e.nodeName.toLowerCase();return(t==="input"||t==="button")&&"reset"===e.type},button:function(e){var t=e.nodeName.toLowerCase();return t==="input"&&"button"===e.type||t==="button"},input:function(e){return/input|select|textarea|button/i.test(e.nodeName)},focus:function(e){return e===e.ownerDocument.activeElement}},setFilters:{first:function(e,t){return t===0},last:function(e,t,n,r){return t===r.length-1},even:function(e,t){return t%2===0},odd:function(e,t){return t%2===1},lt:function(e,t,n){return tn[3]-0},nth:function(e,t,n){return n[3]-0===t},eq:function(e,t,n){return n[3]-0===t}},filter:{PSEUDO:function(e,t,n,r){var i=t[1],s=d.filters[i];if(s)return s(e,n,t,r);if(i==="contains")return(e.textContent||e.innerText||p([e])||"").indexOf(t[3])>=0;if(i==="not"){var o=t[3];for(var u=0,a=o.length;u=0}},ID:function(e,t){return e.nodeType===1&&e.getAttribute("id")===t},TAG:function(e,t){return t==="*"&&e.nodeType===1||!!e.nodeName&&e.nodeName.toLowerCase()===t},CLASS:function(e,t){return(" "+(e.className||e.getAttribute("class"))+" ").indexOf(t)>-1},ATTR:function(e,t){var n=t[1],r=h.attr?h.attr(e,n):d.attrHandle[n]?d.attrHandle[n](e):e[n]!=null?e[n]:e.getAttribute(n),i=r+"",s=t[2],o=t[4];return r==null?s==="!=":!s&&h.attr?r!=null:s==="="?i===o:s==="*="?i.indexOf(o)>=0:s==="~="?(" "+i+" ").indexOf(o)>=0:o?s==="!="?i!==o:s==="^="?i.indexOf(o)===0:s==="$="?i.substr(i.length-o.length)===o:s==="|="?i===o||i.substr(0,o.length+1)===o+"-":!1:i&&r!==!1},POS:function(e,t,n,r){var i=t[2],s=d.setFilters[i];if(s)return s(e,n,t,r)}}},v=d.match.POS,m=function(e,t){return"\\"+(t-0+1)};for(var g in d.match)d.match[g]=new RegExp(d.match[g].source+/(?![^\[]*\])(?![^\(]*\))/.source),d.leftMatch[g]=new RegExp(/(^(?:.|\r|\n)*?)/.source+d.match[g].source.replace(/\\(\d+)/g,m));d.match.globalPOS=v;var y=function(e,t){return e=Array.prototype.slice.call(e,0),t?(t.push.apply(t,e),t):e};try{Array.prototype.slice.call(_.documentElement.childNodes,0)[0].nodeType}catch(b){y=function(e,t){var n=0,r=t||[];if(o.call(e)==="[object Array]")Array.prototype.push.apply(r,e);else if(typeof e.length=="number")for(var i=e.length;n",r.insertBefore(e,r.firstChild),_.getElementById(n)&&(d.find.ID=function(e,n,r){if(typeof n.getElementById!="undefined"&&!r){var i=n.getElementById(e[1]);return i?i.id===e[1]||typeof i.getAttributeNode!="undefined"&&i.getAttributeNode("id").nodeValue===e[1]?[i]:t:[]}},d.filter.ID=function(e,t){var n=typeof e.getAttributeNode!="undefined"&&e.getAttributeNode("id");return e.nodeType===1&&n&&n.nodeValue===t}),r.removeChild(e),r=e=null}(),function(){var e=_.createElement("div");e.appendChild(_.createComment("")),e.getElementsByTagName("*").length>0&&(d.find.TAG=function(e,t){var n=t.getElementsByTagName(e[1]);if(e[1]==="*"){var r=[];for(var i=0;n[i];i++)n[i].nodeType===1&&r.push(n[i]);n=r}return n}),e.innerHTML="",e.firstChild&&typeof e.firstChild.getAttribute!="undefined"&&e.firstChild.getAttribute("href")!=="#"&&(d.attrHandle.href=function(e){return e.getAttribute("href",2)}),e=null}(),_.querySelectorAll&&function(){var e=h,t=_.createElement("div"),n="__sizzle__";t.innerHTML="

      ";if(!t.querySelectorAll||t.querySelectorAll(".TEST").length!==0){h=function(t,r,i,s){r=r||_;if(!s&&!h.isXML(r)){var o=/^(\w+$)|^\.([\w\-]+$)|^#([\w\-]+$)/.exec(t);if(o&&(r.nodeType===1||r.nodeType===9)){if(o[1])return y(r.getElementsByTagName(t),i);if(o[2]&&d.find.CLASS&&r.getElementsByClassName)return y(r.getElementsByClassName(o[2]),i)}if(r.nodeType===9){if(t==="body"&&r.body)return y([r.body],i);if(o&&o[3]){var u=r.getElementById(o[3]);if(!u||!u.parentNode)return y([],i);if(u.id===o[3])return y([u],i)}try{return y(r.querySelectorAll(t),i)}catch(a){}}else if(r.nodeType===1&&r.nodeName.toLowerCase()!=="object"){var f=r,l=r.getAttribute("id"),c=l||n,p=r.parentNode,v=/^\s*[+~]/.test(t);l?c=c.replace(/'/g,"\\$&"):r.setAttribute("id",c),v&&p&&(r=r.parentNode);try{if(!v||p)return y(r.querySelectorAll("[id='"+c+"'] "+t),i)}catch(m){}finally{l||f.removeAttribute("id")}}}return e(t,r,i,s)};for(var r in e)h[r]=e[r];t=null}}(),function(){var e=_.documentElement,t=e.matchesSelector||e.mozMatchesSelector||e.webkitMatchesSelector||e.msMatchesSelector;if(t){var n=!t.call(_.createElement("div"),"div"),r=!1;try{t.call(_.documentElement,"[test!='']:sizzle")}catch(i){r=!0}h.matchesSelector=function(e,i){i=i.replace(/\=\s*([^'"\]]*)\s*\]/g,"='$1']");if(!h.isXML(e))try{if(r||!d.match.PSEUDO.test(i)&&!/!=/.test(i)){var s=t.call(e,i);if(s||!n||e.document&&e.document.nodeType!==11)return s}}catch(o){}return h(i,null,null,[e]).length>0}}}(),function(){var e=_.createElement("div");e.innerHTML="
      ";if(!!e.getElementsByClassName&&e.getElementsByClassName("e").length!==0){e.lastChild.className="e";if(e.getElementsByClassName("e").length===1)return;d.order.splice(1,0,"CLASS"),d.find.CLASS=function(e,t,n){if(typeof t.getElementsByClassName!="undefined"&&!n)return t.getElementsByClassName(e[1])},e=null}}(),_.documentElement.contains?h.contains=function(e,t){return e!==t&&(e.contains?e.contains(t):!0)}:_.documentElement.compareDocumentPosition?h.contains=function(e,t){return!!(e.compareDocumentPosition(t)&16)}:h.contains=function(){return!1},h.isXML=function(e){var t=(e?e.ownerDocument||e:0).documentElement;return t?t.nodeName!=="HTML":!1};var S=function(e,t,n){var r,i=[],s="",o=t.nodeType?[t]:t;while(r=d.match.PSEUDO.exec(e))s+=r[0],e=e.replace(d.match.PSEUDO,"");e=d.relative[e]?e+"*":e;for(var u=0,a=o.length;u0)for(o=s;o=0:H.filter(e,this).length>0:this.filter(e).length>0)},closest:function(e,t){var n=[],r,i,s=this[0];if(H.isArray(e)){var o=1;while(s&&s.ownerDocument&&s!==t){for(r=0;r-1:H.find.matchesSelector(s,e)){n.push(s);break}s=s.parentNode;if(!s||!s.ownerDocument||s===t||s.nodeType===11)break}}return n=n.length>1?H.unique(n):n,this.pushStack(n,"closest",e)},index:function(e){return e?typeof e=="string"?H.inArray(this[0],H(e)):H.inArray(e.jquery?e[0]:e,this):this[0]&&this[0].parentNode?this.prevAll().length:-1},add:function(e,t){var n=typeof e=="string"?H(e,t):H.makeArray(e&&e.nodeType?[e]:e),r=H.merge(this.get(),n);return this.pushStack(N(n[0])||N(r[0])?r:H.unique(r))},andSelf:function(){return this.add(this.prevObject)}}),H.each({parent:function(e){var t=e.parentNode;return t&&t.nodeType!==11?t:null},parents:function(e){return H.dir(e,"parentNode")},parentsUntil:function(e,t,n){return H.dir(e,"parentNode",n)},next:function(e){return H.nth(e,2,"nextSibling")},prev:function(e){return H.nth(e,2,"previousSibling")},nextAll:function(e){return H.dir(e,"nextSibling")},prevAll:function(e){return H.dir(e,"previousSibling")},nextUntil:function(e,t,n){return H.dir(e,"nextSibling",n)},prevUntil:function(e,t,n){return H.dir(e,"previousSibling",n)},siblings:function(e){return H.sibling((e.parentNode||{}).firstChild,e)},children:function(e){return H.sibling(e.firstChild)},contents:function(e){return H.nodeName(e,"iframe")?e.contentDocument||e.contentWindow.document:H.makeArray(e.childNodes)}},function(e,t){H.fn[e]=function(n,r){var i=H.map(this,t,n);return ut.test(e)||(r=n),r&&typeof r=="string"&&(i=H.filter(r,i)),i=this.length>1&&!pt[e]?H.unique(i):i,(this.length>1||ft.test(r))&&at.test(e)&&(i=i.reverse()),this.pushStack(i,e,ct.call(arguments).join(","))}}),H.extend({filter:function(e,t,n){return n&&(e=":not("+e+")"),t.length===1?H.find.matchesSelector(t[0],e)?[t[0]]:[]:H.find.matches(e,t)},dir:function(e,n,r){var i=[],s=e[n];while(s&&s.nodeType!==9&&(r===t||s.nodeType!==1||!H(s).is(r)))s.nodeType===1&&i.push(s),s=s[n];return i},nth:function(e,t,n,r){t=t||1;var i=0;for(;e;e=e[n])if(e.nodeType===1&&++i===t)break;return e},sibling:function(e,t){var n=[];for(;e;e=e.nextSibling)e.nodeType===1&&e!==t&&n.push(e);return n}});var dt="abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",vt=/ jQuery\d+="(?:\d+|null)"/g,mt=/^\s+/,gt=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig,yt=/<([\w:]+)/,bt=/]","i"),Tt=/checked\s*(?:[^=]|=\s*.checked.)/i,Nt=/\/(java|ecma)script/i,Ct=/^\s*",""],legend:[1,"
      ","
      "],thead:[1,"","
      "],tr:[2,"","
      "],td:[3,"","
      "],col:[2,"","
      "],area:[1,"",""],_default:[0,"",""]},Lt=x(_);kt.optgroup=kt.option,kt.tbody=kt.tfoot=kt.colgroup=kt.caption=kt.thead,kt.th=kt.td,H.support.htmlSerialize||(kt._default=[1,"div
      ","
      "]),H.fn.extend({text:function(e){return H.access(this,function(e){return e===t?H.text(this):this.empty().append((this[0]&&this[0].ownerDocument||_).createTextNode(e))},null,e,arguments.length)},wrapAll:function(e){if(H.isFunction(e))return this.each(function(t){H(this).wrapAll(e.call(this,t))});if(this[0]){var t=H(e,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&t.insertBefore(this[0]),t.map(function(){var e=this;while(e.firstChild&&e.firstChild.nodeType===1)e=e.firstChild;return e}).append(this)}return this},wrapInner:function(e){return H.isFunction(e)?this.each(function(t){H(this).wrapInner(e.call(this,t))}):this.each(function(){var t=H(this),n=t.contents();n.length?n.wrapAll(e):t.append(e)})},wrap:function(e){var t=H.isFunction(e);return this.each(function(n){H(this).wrapAll(t?e.call(this,n):e)})},unwrap:function(){return this.parent().each(function(){H.nodeName(this,"body")||H(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,!0,function(e){this.nodeType===1&&this.appendChild(e)})},prepend:function(){return this.domManip(arguments,!0,function(e){this.nodeType===1&&this.insertBefore(e,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(e){this.parentNode.insertBefore(e,this)});if(arguments.length){var e=H.clean(arguments);return e.push.apply(e,this.toArray()),this.pushStack(e,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(e){this.parentNode.insertBefore(e,this.nextSibling)});if(arguments.length){var e= +this.pushStack(this,"after",arguments);return e.push.apply(e,H.clean(arguments)),e}},remove:function(e,t){for(var n=0,r;(r=this[n])!=null;n++)if(!e||H.filter(e,[r]).length)!t&&r.nodeType===1&&(H.cleanData(r.getElementsByTagName("*")),H.cleanData([r])),r.parentNode&&r.parentNode.removeChild(r);return this},empty:function(){for(var e=0,t;(t=this[e])!=null;e++){t.nodeType===1&&H.cleanData(t.getElementsByTagName("*"));while(t.firstChild)t.removeChild(t.firstChild)}return this},clone:function(e,t){return e=e==null?!1:e,t=t==null?e:t,this.map(function(){return H.clone(this,e,t)})},html:function(e){return H.access(this,function(e){var n=this[0]||{},r=0,i=this.length;if(e===t)return n.nodeType===1?n.innerHTML.replace(vt,""):null;if(typeof e=="string"&&!Et.test(e)&&(H.support.leadingWhitespace||!mt.test(e))&&!kt[(yt.exec(e)||["",""])[1].toLowerCase()]){e=e.replace(gt,"<$1>");try{for(;r1&&l0?this.clone(!0):this).get();H(i[o])[t](a),r=r.concat(a)}return this.pushStack(r,e,i.selector)}}),H.extend({clone:function(e,t,n){var r,i,s,o=H.support.html5Clone||H.isXMLDoc(e)||!xt.test("<"+e.nodeName+">")?e.cloneNode(!0):m(e);if((!H.support.noCloneEvent||!H.support.noCloneChecked)&&(e.nodeType===1||e.nodeType===11)&&!H.isXMLDoc(e)){w(e,o),r=b(e),i=b(o);for(s=0;r[s];++s)i[s]&&w(r[s],i[s])}if(t){E(e,o);if(n){r=b(e),i=b(o);for(s=0;r[s];++s)E(r[s],i[s])}}return r=i=null,o},clean:function(e,t,n,r){var i,s,o,u=[];t=t||_,typeof t.createElement=="undefined"&&(t=t.ownerDocument||t[0]&&t[0].ownerDocument||_);for(var a=0,f;(f=e[a])!=null;a++){typeof f=="number"&&(f+="");if(!f)continue;if(typeof f=="string")if(!wt.test(f))f=t.createTextNode(f);else{f=f.replace(gt,"<$1>");var l=(yt.exec(f)||["",""])[1].toLowerCase(),c=kt[l]||kt._default,h=c[0],p=t.createElement("div"),d=Lt.childNodes,v;t===_?Lt.appendChild(p):x(t).appendChild(p),p.innerHTML=c[1]+f+c[2];while(h--)p=p.lastChild;if(!H.support.tbody){var m=bt.test(f),y=l==="table"&&!m?p.firstChild&&p.firstChild.childNodes:c[1]===""&&!m?p.childNodes:[];for(o=y.length-1;o>=0;--o)H.nodeName(y[o],"tbody")&&!y[o].childNodes.length&&y[o].parentNode.removeChild(y[o])}!H.support.leadingWhitespace&&mt.test(f)&&p.insertBefore(t.createTextNode(mt.exec(f)[0]),p.firstChild),f=p.childNodes,p&&(p.parentNode.removeChild(p),d.length>0&&(v=d[d.length-1],v&&v.parentNode&&v.parentNode.removeChild(v)))}var b;if(!H.support.appendChecked)if(f[0]&&typeof (b=f.length)=="number")for(o=0;o1)},H.extend({cssHooks:{opacity:{get:function(e,t){if(t){var n=Ft(e,"opacity");return n===""?"1":n}return e.style.opacity}}},cssNumber:{fillOpacity:!0,fontWeight:!0,lineHeight:!0,opacity:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":H.support.cssFloat?"cssFloat":"styleFloat"},style:function(e,n,r,i){if(!!e&&e.nodeType!==3&&e.nodeType!==8&&!!e.style){var s,o,u=H.camelCase(n),a=e.style,f=H.cssHooks[u];n=H.cssProps[u]||u;if(r===t)return f&&"get"in f&&(s=f.get(e,!1,i))!==t?s:a[n];o=typeof r,o==="string"&&(s=Pt.exec(r))&&(r=+(s[1]+1)*+s[2]+parseFloat(H.css(e,n)),o="number");if(r==null||o==="number"&&isNaN(r))return;o==="number"&&!H.cssNumber[u]&&(r+="px");if(!f||!("set"in f)||(r=f.set(e,r))!==t)try{a[n]=r}catch(l){}}},css:function(e,n,r){var i,s;n=H.camelCase(n),s=H.cssHooks[n],n=H.cssProps[n]||n,n==="cssFloat"&&(n="float");if(s&&"get"in s&&(i=s.get(e,!0,r))!==t)return i;if(Ft)return Ft(e,n)},swap:function(e,t,n){var r={},i,s;for(s in t)r[s]=e.style[s],e.style[s]=t[s];i=n.call(e);for(s in t)e.style[s]=r[s];return i}}),H.curCSS=H.css,_.defaultView&&_.defaultView.getComputedStyle&&(It=function(e,t){var n,r,i,s,o=e.style;return t=t.replace(Mt,"-$1").toLowerCase(),(r=e.ownerDocument.defaultView)&&(i=r.getComputedStyle(e,null))&&(n=i.getPropertyValue(t),n===""&&!H.contains(e.ownerDocument.documentElement,e)&&(n=H.style(e,t))),!H.support.pixelMargin&&i&&Ht.test(t)&&Dt.test(n)&&(s=o.width,o.width=n,n=i.width,o.width=s),n}),_.documentElement.currentStyle&&(qt=function(e,t){var n,r,i,s=e.currentStyle&&e.currentStyle[t],o=e.style;return s==null&&o&&(i=o[t])&&(s=i),Dt.test(s)&&(n=o.left,r=e.runtimeStyle&&e.runtimeStyle.left,r&&(e.runtimeStyle.left=e.currentStyle.left),o.left=t==="fontSize"?"1em":s,s=o.pixelLeft+"px",o.left=n,r&&(e.runtimeStyle.left=r)),s===""?"auto":s}),Ft=It||qt,H.each(["height","width"],function(e,t){H.cssHooks[t]={get:function(e,n,r){if(n)return e.offsetWidth!==0?v(e,t,r):H.swap(e,Bt,function(){return v(e,t,r)})},set:function(e,t){return _t.test(t)?t+"px":t}}}),H.support.opacity||(H.cssHooks.opacity={get:function(e,t){return Ot.test((t&&e.currentStyle?e.currentStyle.filter:e.style.filter)||"")?parseFloat(RegExp.$1)/100+"":t?"1":""},set:function(e,t){var n=e.style,r=e.currentStyle,i=H.isNumeric(t)?"alpha(opacity="+t*100+")":"",s=r&&r.filter||n.filter||"";n.zoom=1;if(t>=1&&H.trim(s.replace(At,""))===""){n.removeAttribute("filter");if(r&&!r.filter)return}n.filter=At.test(s)?s.replace(At,i):s+" "+i}}),H(function(){H.support.reliableMarginRight||(H.cssHooks.marginRight={get:function(e,t){return H.swap(e,{display:"inline-block"},function(){return t?Ft(e,"margin-right"):e.style.marginRight})}})}),H.expr&&H.expr.filters&&(H.expr.filters.hidden=function(e){var t=e.offsetWidth,n=e.offsetHeight;return t===0&&n===0||!H.support.reliableHiddenOffsets&&(e.style&&e.style.display||H.css(e,"display"))==="none"},H.expr.filters.visible=function(e){return!H.expr.filters.hidden(e)}),H.each({margin:"",padding:"",border:"Width"},function(e,t){H.cssHooks[e+t]={expand:function(n){var r,i=typeof n=="string"?n.split(" "):[n],s={};for(r=0;r<4;r++)s[e+jt[r]+t]=i[r]||i[r-2]||i[0];return s}}});var Rt=/%20/g,Ut=/\[\]$/,zt=/\r?\n/g,Wt=/#.*$/,Xt=/^(.*?):[ \t]*([^\r\n]*)\r?$/mg,Vt=/^(?:color|date|datetime|datetime-local|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,$t=/^(?:about|app|app\-storage|.+\-extension|file|res|widget):$/,Jt=/^(?:GET|HEAD)$/,Kt=/^\/\//,Qt=/\?/,Gt=/)<[^<]*)*<\/script>/gi,Yt=/^(?:select|textarea)/i,Zt=/\s+/,en=/([?&])_=[^&]*/,tn=/^([\w\+\.\-]+:)(?:\/\/([^\/?#:]*)(?::(\d+))?)?/,nn=H.fn.load,rn={},sn={},on,un,an=["*/"]+["*"];try{on=P.href}catch(fn){on=_.createElement("a"),on.href="",on=on.href}un=tn.exec(on.toLowerCase())||[],H.fn.extend({load:function(e,n,r){if(typeof e!="string"&&nn)return nn.apply(this,arguments);if(!this.length)return this;var i=e.indexOf(" ");if(i>=0){var s=e.slice(i,e.length);e=e.slice(0,i)}var o="GET";n&&(H.isFunction(n)?(r=n,n=t):typeof n=="object"&&(n=H.param(n,H.ajaxSettings.traditional),o="POST"));var u=this;return H.ajax({url:e,type:o,dataType:"html",data:n,complete:function(e,t,n){n=e.responseText,e.isResolved()&&(e.done(function(e){n=e}),u.html(s?H("
      ").append(n.replace(Gt,"")).find(s):n)),r&&u.each(r,[n,t,e])}}),this},serialize:function(){return H.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?H.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||Yt.test(this.nodeName)||Vt.test(this.type))}).map(function(e,t){var n=H(this).val();return n==null?null:H.isArray(n)?H.map(n,function(e,n){return{name:t.name,value:e.replace(zt,"\r\n")}}):{name:t.name,value:n.replace(zt,"\r\n")}}).get()}}),H.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),function(e,t){H.fn[t]=function(e){return this.on(t,e)}}),H.each(["get","post"],function(e,n){H[n]=function(e,r,i,s){return H.isFunction(r)&&(s=s||i,i=r,r=t),H.ajax({type:n,url:e,data:r,success:i,dataType:s})}}),H.extend({getScript:function(e,n){return H.get(e,t,n,"script")},getJSON:function(e,t,n){return H.get(e,t,n,"json")},ajaxSetup:function(e,t){return t?h(e,H.ajaxSettings):(t=e,e=H.ajaxSettings),h(e,t),e},ajaxSettings:{url:on,isLocal:$t.test(un[1]),global:!0,type:"GET",contentType:"application/x-www-form-urlencoded; charset=UTF-8",processData:!0,async:!0,accepts:{xml:"application/xml, text/xml",html:"text/html",text:"text/plain",json:"application/json, text/javascript","*":an},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText"},converters:{"* text":e.String,"text html":!0,"text json":H.parseJSON,"text xml":H.parseXML},flatOptions:{context:!0,url:!0}},ajaxPrefilter:d(rn),ajaxTransport:d(sn),ajax:function(e,n){function r(e,n,r,p){if(E!==2){E=2,b&&clearTimeout(b),y=t,m=p||"",T.readyState=e>0?4:0;var d,v,g,w=n,x=r?l(i,T,r):t,N,C;if(e>=200&&e<300||e===304){if(i.ifModified){if(N=T.getResponseHeader("Last-Modified"))H.lastModified[h]=N;if(C=T.getResponseHeader("Etag"))H.etag[h]=C}if(e===304)w="notmodified",d=!0;else try{v=f(i,x),w="success",d=!0}catch(k){w="parsererror",g=k}}else{g=w;if(!w||e)w="error",e<0&&(e=0)}T.status=e,T.statusText=""+(n||w),d?u.resolveWith(s,[v,w,T]):u.rejectWith(s,[T,w,g]),T.statusCode(c),c=t,S&&o.trigger("ajax"+(d?"Success":"Error"),[T,i,d?v:g]),a.fireWith(s,[T,w]),S&&(o.trigger("ajaxComplete",[T,i]),--H.active||H.event.trigger("ajaxStop"))}}typeof e=="object"&&(n=e,e=t),n=n||{};var i=H.ajaxSetup({},n),s=i.context||i,o=s!==i&&(s.nodeType||s instanceof H)?H(s):H.event,u=H.Deferred(),a=H.Callbacks("once memory"),c=i.statusCode||{},h,d={},v={},m,g,y,b,w,E=0,S,x,T={readyState:0,setRequestHeader:function(e,t){if(!E){var n=e.toLowerCase();e=v[n]=v[n]||e,d[e]=t}return this},getAllResponseHeaders:function(){return E===2?m:null},getResponseHeader:function(e){var n;if(E===2){if(!g){g={};while(n=Xt.exec(m))g[n[1].toLowerCase()]=n[2]}n=g[e.toLowerCase()]}return n===t?null:n},overrideMimeType:function(e){return E||(i.mimeType=e),this},abort:function(e){return e=e||"abort",y&&y.abort(e),r(0,e),this}};u.promise(T),T.success=T.done,T.error=T.fail,T.complete=a.add,T.statusCode=function(e){if(e){var t;if(E<2)for(t in e)c[t]=[c[t],e[t]];else t=e[T.status],T.then(t,t)}return this},i.url=((e||i.url)+"").replace(Wt,"").replace(Kt,un[1]+"//"),i.dataTypes=H.trim(i.dataType||"*").toLowerCase().split(Zt),i.crossDomain==null&&(w=tn.exec(i.url.toLowerCase()),i.crossDomain=!(!w||w[1]==un[1]&&w[2]==un[2]&&(w[3]||(w[1]==="http:"?80:443))==(un[3]||(un[1]==="http:"?80:443)))),i.data&&i.processData&&typeof i.data!="string"&&(i.data=H.param(i.data,i.traditional)),p(rn,i,n,T);if(E===2)return!1;S=i.global,i.type=i.type.toUpperCase(),i.hasContent=!Jt.test(i.type),S&&H.active++===0&&H.event.trigger("ajaxStart");if(!i.hasContent){i.data&&(i.url+=(Qt.test(i.url)?"&":"?")+i.data,delete i.data),h=i.url;if(i.cache===!1){var N=H.now(),C=i.url.replace(en,"$1_="+N);i.url=C+(C===i.url?(Qt.test(i.url)?"&":"?")+"_="+N:"")}}(i.data&&i.hasContent&&i.contentType!==!1||n.contentType)&&T.setRequestHeader("Content-Type",i.contentType),i.ifModified&&(h=h||i.url,H.lastModified[h]&&T.setRequestHeader("If-Modified-Since",H.lastModified[h]),H.etag[h]&&T.setRequestHeader("If-None-Match",H.etag[h])),T.setRequestHeader("Accept",i.dataTypes[0]&&i.accepts[i.dataTypes[0]]?i.accepts[i.dataTypes[0]]+(i.dataTypes[0]!=="*"?", "+an+"; q=0.01":""):i.accepts["*"]);for(x in i.headers)T.setRequestHeader(x,i.headers[x]);if(!i.beforeSend||i.beforeSend.call(s,T,i)!==!1&&E!==2){for(x in{success:1,error:1,complete:1})T[x](i[x]);y=p(sn,i,n,T);if(!y)r(-1,"No Transport");else{T.readyState=1,S&&o.trigger("ajaxSend",[T,i]),i.async&&i.timeout>0&&(b=setTimeout(function(){T.abort("timeout")},i.timeout));try{E=1,y.send(d,r)}catch(k){if(!(E<2))throw k;r(-1,k)}}return T}return T.abort(),!1},param:function(e,n){var r=[],i=function(e,t){t=H.isFunction(t)?t():t,r[r.length]=encodeURIComponent(e)+"="+encodeURIComponent(t)};n===t&&(n=H.ajaxSettings.traditional);if(H.isArray(e)||e.jquery&&!H.isPlainObject(e))H.each(e,function(){i(this.name,this.value)});else for(var s in e)c(s,e[s],n,i);return r.join("&").replace(Rt,"+")}}),H.extend({active:0,lastModified:{},etag:{}});var ln=H.now(),cn=/(\=)\?(&|$)|\?\?/i;H.ajaxSetup({jsonp:"callback",jsonpCallback:function(){return H.expando+"_"+ln++}}),H.ajaxPrefilter("json jsonp",function(t,n,r){var i=typeof t.data=="string"&&/^application\/x\-www\-form\-urlencoded/.test(t.contentType);if(t.dataTypes[0]==="jsonp"||t.jsonp!==!1&&(cn.test(t.url)||i&&cn.test(t.data))){var s,o=t.jsonpCallback=H.isFunction(t.jsonpCallback)?t.jsonpCallback():t.jsonpCallback,u=e[o],a=t.url,f=t.data,l="$1"+o+"$2";return t.jsonp!==!1&&(a=a.replace(cn,l),t.url===a&&(i&&(f=f.replace(cn,l)),t.data===f&&(a+=(/\?/.test(a)?"&":"?")+t.jsonp+"="+o))),t.url=a,t.data=f,e[o]=function(e){s=[e]},r.always(function(){e[o]=u,s&&H.isFunction(u)&&e[o](s[0])}),t.converters["script json"]=function(){return s||H.error(o+" was not called"),s[0]},t.dataTypes[0]="json","script"}}),H.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/javascript|ecmascript/},converters:{"text script":function(e){return H.globalEval(e),e}}}),H.ajaxPrefilter("script",function(e){e.cache===t&&(e.cache=!1),e.crossDomain&&(e.type="GET",e.global=!1)}),H.ajaxTransport("script",function(e){if(e.crossDomain){var n,r=_.head||_.getElementsByTagName("head")[0]||_.documentElement;return{send:function(i,s){n=_.createElement("script"),n.async="async",e.scriptCharset&&(n.charset=e.scriptCharset),n.src=e.url,n.onload=n.onreadystatechange=function(e,i){if(i||!n.readyState||/loaded|complete/.test(n.readyState))n.onload=n.onreadystatechange=null,r&&n.parentNode&&r.removeChild(n),n=t,i||s(200,"success")},r.insertBefore(n,r.firstChild)},abort:function(){n&&n.onload(0,1)}}}});var hn=e.ActiveXObject?function(){for(var e in dn)dn[e](0,1)}:!1,pn=0,dn;H.ajaxSettings.xhr=e.ActiveXObject?function(){return!this.isLocal&&a()||u()}:a,function(e){H.extend(H.support,{ajax:!!e,cors:!!e&&"withCredentials"in e})}(H.ajaxSettings.xhr()),H.support.ajax&&H.ajaxTransport(function(n){if(!n.crossDomain||H.support.cors){var r;return{send:function(i,s){var o=n.xhr(),u,a;n.username?o.open(n.type,n.url,n.async,n.username,n.password):o.open(n.type,n.url,n.async);if(n.xhrFields)for(a in n.xhrFields)o[a]=n.xhrFields[a];n.mimeType&&o.overrideMimeType&&o.overrideMimeType(n.mimeType),!n.crossDomain&&!i["X-Requested-With"]&&(i["X-Requested-With"]="XMLHttpRequest");try{for(a in i)o.setRequestHeader(a,i[a])}catch(f){}o.send(n.hasContent&&n.data||null),r=function(e,i){var a,f,l,c,h;try{if(r&&(i||o.readyState===4)){r=t,u&&(o.onreadystatechange=H.noop,hn&&delete dn[u]);if(i)o.readyState!==4&&o.abort();else{a=o.status,l=o.getAllResponseHeaders(),c={},h=o.responseXML,h&&h.documentElement&&(c.xml=h);try{c.text=o.responseText}catch(e){}try{f=o.statusText}catch(p){f=""}!a&&n.isLocal&&!n.crossDomain?a=c.text?200:404:a===1223&&(a=204)}}}catch(d){i||s(-1,d)}c&&s(a,f,c,l)},!n.async||o.readyState===4?r():(u=++pn,hn&&(dn||(dn={},H(e).unload(hn)),dn[u]=r),o.onreadystatechange=r)},abort:function(){r&&r(0,1)}}}});var vn={},mn,gn,yn=/^(?:toggle|show|hide)$/,bn=/^([+\-]=)?([\d+.\-]+)([a-z%]*)$/i,wn,En=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]],Sn;H.fn.extend({show:function(e,t,n){var s,o;if(e||e===0)return this.animate(i("show",3),e,t,n);for(var u=0,a=this.length;u=a.duration+this.startTime){this.now=this.end,this.pos=this.state=1,this.update(),a.animatedProperties[this.prop]=!0;for(t in a.animatedProperties)a.animatedProperties[t]!==!0&&(s=!1);if(s){a.overflow!=null&&!H.support.shrinkWrapBlocks&&H.each(["","X","Y"],function(e,t){u.style["overflow"+t]=a.overflow[e]}),a.hide&&H(u).hide();if(a.hide||a.show)for(t in a.animatedProperties)H.style(u,t,a.orig[t]),H.removeData(u,"fxshow"+t,!0),H.removeData(u,"toggle"+t,!0);r=a.complete,r&&(a.complete=!1,r.call(u))}return!1}return a.duration==Infinity?this.now=i:(n=i-this.startTime,this.state=n/a.duration,this.pos=H.easing[a.animatedProperties[this.prop]](this.state,n,0,1,a.duration),this.now=this.start+(this.end-this.start)*this.pos),this.update(),!0}},H.extend(H.fx,{tick:function(){var e,t=H.timers,n=0;for(;n-1,f={},l={},c,h;a?(l=i.position(),c=l.top,h=l.left):(c=parseFloat(o)||0,h=parseFloat(u)||0),H.isFunction(t)&&(t=t.call(e,n,s)),t.top!=null&&(f.top=t.top-s.top+c),t.left!=null&&(f.left=t.left-s.left+h),"using"in t?t.using.call(e,f):i.css(f)}},H.fn.extend({position:function(){if(!this[0])return null;var e=this[0],t=this.offsetParent(),n=this.offset(),r=Nn.test(t[0].nodeName)?{top:0,left:0}:t.offset();return n.top-=parseFloat(H.css(e,"marginTop"))||0,n.left-=parseFloat(H.css(e,"marginLeft"))||0,r.top+=parseFloat(H.css(t[0],"borderTopWidth"))||0,r.left+=parseFloat(H.css(t[0],"borderLeftWidth"))||0,{top:n.top-r.top,left:n.left-r.left}},offsetParent:function(){return this.map(function(){var e=this.offsetParent||_.body;while(e&&!Nn.test(e.nodeName)&&H.css(e,"position")==="static")e=e.offsetParent;return e})}}),H.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(e,r){var i=/Y/.test(r);H.fn[e]=function(s){return H.access(this,function(e,s,o){var u=n(e);if(o===t)return u?r in u?u[r]:H.support.boxModel&&u.document.documentElement[s]||u.document.body[s]:e[s];u?u.scrollTo(i?H(u).scrollLeft():o,i?o:H(u).scrollTop()):e[s]=o},e,s,arguments.length,null)}}),H.each({Height:"height",Width:"width"},function(e,n){var r="client"+e,i="scroll"+e,s="offset"+e;H.fn["inner"+e]=function(){var e=this[0];return e?e.style?parseFloat(H.css(e,n,"padding")):this[n]():null},H.fn["outer"+e]=function(e){var t=this[0];return t?t.style?parseFloat(H.css(t,n,e?"margin":"border")):this[n]():null},H.fn[n]=function(e){return H.access(this,function(e,n,o){var u,a,f,l;if(H.isWindow(e))return u=e.document,a=u.documentElement[r],H.support.boxModel&&a||u.body&&u.body[r]||a;if(e.nodeType===9)return u=e.documentElement,u[r]>=u[i]?u[r]:Math.max(e.body[i],u[i],e.body[s],u[s]);if(o===t)return f=H.css(e,n),l=parseFloat(f),H.isNumeric(l)?l:f;H(e).css(n,o)},n,e,arguments.length,null)}}),e.jQuery=e.$=H,typeof define=="function"&&define.amd&&define.amd.jQuery&&define("jquery",[],function(){return H})})(window),function(){var e=this,t=e._,n={},r=Array.prototype,i=Object.prototype,s=Function.prototype,o=r.push,u=r.slice,a=r.concat,f=i.toString,l=i.hasOwnProperty,c=r.forEach,h=r.map,p=r.reduce,d=r.reduceRight,v=r.filter,m=r.every,g=r.some,y=r.indexOf,b=r.lastIndexOf,w=Array.isArray,E=Object.keys,S=s.bind,x=function(e){return e instanceof x?e:this instanceof x?(this._wrapped=e,void 0):new x(e)};"undefined"!=typeof exports?("undefined"!=typeof module&&module.exports&&(exports=module.exports=x),exports._=x):e._=x,x.VERSION="1.4.4";var T=x.each=x.forEach=function(e,t,r){if(null!=e)if(c&&e.forEach===c)e.forEach(t,r);else if(e.length===+e.length){for(var i=0,s=e.length;s>i;i++)if(t.call(r,e[i],i,e)===n)return}else for(var o in e)if(x.has(e,o)&&t.call(r,e[o],o,e)===n)return};x.map=x.collect=function(e,t,n){var r=[];return null==e?r:h&&e.map===h?e.map(t,n):(T(e,function(e,i,s){r[r.length]=t.call(n,e,i,s)}),r)};var N="Reduce of empty array with no initial value";x.reduce=x.foldl=x.inject=function(e,t,n,r){var i=arguments.length>2;if(null==e&&(e=[]),p&&e.reduce===p)return r&&(t=x.bind(t,r)),i?e.reduce(t,n):e.reduce(t);if(T(e,function(e,s,o){i?n=t.call(r,n,e,s,o):(n=e,i=!0)}),!i)throw new TypeError(N);return n},x.reduceRight=x.foldr=function(e,t,n,r){var i=arguments.length>2;if(null==e&&(e=[]),d&&e.reduceRight===d)return r&&(t=x.bind(t,r)),i?e.reduceRight(t,n):e.reduceRight(t);var s=e.length;if(s!==+s){var o=x.keys(e);s=o.length}if(T(e,function(u,a,f){a=o?o[--s]:--s,i?n=t.call(r,n,e[a],a,f):(n=e[a],i=!0)}),!i)throw new TypeError(N);return n},x.find=x.detect=function(e,t,n){var r;return C(e,function(e,i,s){return t.call(n,e,i,s)?(r=e,!0):void 0}),r},x.filter=x.select=function(e,t,n){var r=[];return null==e?r:v&&e.filter===v?e.filter(t,n):(T(e,function(e,i,s){t.call(n,e,i,s)&&(r[r.length]=e)}),r)},x.reject=function(e,t,n){return x.filter(e,function(e,r,i){return!t.call(n,e,r,i)},n)},x.every=x.all=function(e,t,r){t||(t=x.identity);var i=!0;return null==e?i:m&&e.every===m?e.every(t,r):(T(e,function(e,s,o){return(i=i&&t.call(r,e,s,o))?void 0:n}),!!i)};var C=x.some=x.any=function(e,t,r){t||(t=x.identity);var i=!1;return null==e?i:g&&e.some===g?e.some(t,r):(T(e,function(e,s,o){return i||(i=t.call(r,e,s,o))?n:void 0}),!!i)};x.contains=x.include=function(e,t){return null==e?!1:y&&e.indexOf===y?e.indexOf(t)!=-1:C(e,function(e){return e===t})},x.invoke=function(e,t){var n=u.call(arguments,2),r=x.isFunction(t);return x.map(e,function(e){return(r?t:e[t]).apply(e,n)})},x.pluck=function(e,t){return x.map(e,function(e){return e[t]})},x.where=function(e,t,n){return x.isEmpty(t)?n?null:[]:x[n?"find":"filter"](e,function(e){for(var n in t)if(t[n]!==e[n])return!1;return!0})},x.findWhere=function(e,t){return x.where(e,t,!0)},x.max=function(e,t,n){if(!t&&x.isArray(e)&&e[0]===+e[0]&&65535>e.length)return Math.max.apply(Math,e);if(!t&&x.isEmpty(e))return-1/0;var r={computed:-1/0,value:-1/0};return T(e,function(e,i,s){var o=t?t.call(n,e,i,s):e;o>=r.computed&&(r={value:e,computed:o})}),r.value},x.min=function(e,t,n){if(!t&&x.isArray(e)&&e[0]===+e[0]&&65535>e.length)return Math.min.apply(Math,e);if(!t&&x.isEmpty(e))return 1/0;var r={computed:1/0,value:1/0};return T(e,function(e,i,s){var o=t?t.call(n,e,i,s):e;r.computed>o&&(r={value:e,computed:o})}),r.value},x.shuffle=function(e){var t,n=0,r=[];return T(e,function(e){t=x.random(n++),r[n-1]=r[t],r[t]=e}),r};var k=function(e){return x.isFunction(e)?e:function(t){return t[e]}};x.sortBy=function(e,t,n){var r=k(t);return x.pluck(x.map(e,function(e,t,i){return{value:e +,index:t,criteria:r.call(n,e,t,i)}}).sort(function(e,t){var n=e.criteria,r=t.criteria;if(n!==r){if(n>r||n===void 0)return 1;if(r>n||r===void 0)return-1}return e.indexs;){var u=s+o>>>1;i>n.call(r,e[u])?s=u+1:o=u}return s},x.toArray=function(e){return e?x.isArray(e)?u.call(e):e.length===+e.length?x.map(e,x.identity):x.values(e):[]},x.size=function(e){return null==e?0:e.length===+e.length?e.length:x.keys(e).length},x.first=x.head=x.take=function(e,t,n){return null==e?void 0:null==t||n?e[0]:u.call(e,0,t)},x.initial=function(e,t,n){return u.call(e,0,e.length-(null==t||n?1:t))},x.last=function(e,t,n){return null==e?void 0:null==t||n?e[e.length-1]:u.call(e,Math.max(e.length-t,0))},x.rest=x.tail=x.drop=function(e,t,n){return u.call(e,null==t||n?1:t)},x.compact=function(e){return x.filter(e,x.identity)};var A=function(e,t,n){return T(e,function(e){x.isArray(e)?t?o.apply(n,e):A(e,t,n):n.push(e)}),n};x.flatten=function(e,t){return A(e,t,[])},x.without=function(e){return x.difference(e,u.call(arguments,1))},x.uniq=x.unique=function(e,t,n,r){x.isFunction(t)&&(r=n,n=t,t=!1);var i=n?x.map(e,n,r):e,s=[],o=[];return T(i,function(n,r){(t?r&&o[o.length-1]===n:x.contains(o,n))||(o.push(n),s.push(e[r]))}),s},x.union=function(){return x.uniq(a.apply(r,arguments))},x.intersection=function(e){var t=u.call(arguments,1);return x.filter(x.uniq(e),function(e){return x.every(t,function(t){return x.indexOf(t,e)>=0})})},x.difference=function(e){var t=a.apply(r,u.call(arguments,1));return x.filter(e,function(e){return!x.contains(t,e)})},x.zip=function(){for(var e=u.call(arguments),t=x.max(x.pluck(e,"length")),n=Array(t),r=0;t>r;r++)n[r]=x.pluck(e,""+r);return n},x.object=function(e,t){if(null==e)return{};for(var n={},r=0,i=e.length;i>r;r++)t?n[e[r]]=t[r]:n[e[r][0]]=e[r][1];return n},x.indexOf=function(e,t,n){if(null==e)return-1;var r=0,i=e.length;if(n){if("number"!=typeof n)return r=x.sortedIndex(e,t),e[r]===t?r:-1;r=0>n?Math.max(0,i+n):n}if(y&&e.indexOf===y)return e.indexOf(t,n);for(;i>r;r++)if(e[r]===t)return r;return-1},x.lastIndexOf=function(e,t,n){if(null==e)return-1;var r=null!=n;if(b&&e.lastIndexOf===b)return r?e.lastIndexOf(t,n):e.lastIndexOf(t);for(var i=r?n:e.length;i--;)if(e[i]===t)return i;return-1},x.range=function(e,t,n){1>=arguments.length&&(t=e||0,e=0),n=arguments[2]||1;for(var r=Math.max(Math.ceil((t-e)/n),0),i=0,s=Array(r);r>i;)s[i++]=e,e+=n;return s},x.bind=function(e,t){if(e.bind===S&&S)return S.apply(e,u.call(arguments,1));var n=u.call(arguments,2);return function(){return e.apply(t,n.concat(u.call(arguments)))}},x.partial=function(e){var t=u.call(arguments,1);return function(){return e.apply(this,t.concat(u.call(arguments)))}},x.bindAll=function(e){var t=u.call(arguments,1);return 0===t.length&&(t=x.functions(e)),T(t,function(t){e[t]=x.bind(e[t],e)}),e},x.memoize=function(e,t){var n={};return t||(t=x.identity),function(){var r=t.apply(this,arguments);return x.has(n,r)?n[r]:n[r]=e.apply(this,arguments)}},x.delay=function(e,t){var n=u.call(arguments,2);return setTimeout(function(){return e.apply(null,n)},t)},x.defer=function(e){return x.delay.apply(x,[e,1].concat(u.call(arguments,1)))},x.throttle=function(e,t){var n,r,i,s,o=0,u=function(){o=new Date,i=null,s=e.apply(n,r)};return function(){var a=new Date,f=t-(a-o);return n=this,r=arguments,0>=f?(clearTimeout(i),i=null,o=a,s=e.apply(n,r)):i||(i=setTimeout(u,f)),s}},x.debounce=function(e,t,n){var r,i;return function(){var s=this,o=arguments,u=function(){r=null,n||(i=e.apply(s,o))},a=n&&!r;return clearTimeout(r),r=setTimeout(u,t),a&&(i=e.apply(s,o)),i}},x.once=function(e){var t,n=!1;return function(){return n?t:(n=!0,t=e.apply(this,arguments),e=null,t)}},x.wrap=function(e,t){return function(){var n=[e];return o.apply(n,arguments),t.apply(this,n)}},x.compose=function(){var e=arguments;return function(){for(var t=arguments,n=e.length-1;n>=0;n--)t=[e[n].apply(this,t)];return t[0]}},x.after=function(e,t){return 0>=e?t():function(){return 1>--e?t.apply(this,arguments):void 0}},x.keys=E||function(e){if(e!==Object(e))throw new TypeError("Invalid object");var t=[];for(var n in e)x.has(e,n)&&(t[t.length]=n);return t},x.values=function(e){var t=[];for(var n in e)x.has(e,n)&&t.push(e[n]);return t},x.pairs=function(e){var t=[];for(var n in e)x.has(e,n)&&t.push([n,e[n]]);return t},x.invert=function(e){var t={};for(var n in e)x.has(e,n)&&(t[e[n]]=n);return t},x.functions=x.methods=function(e){var t=[];for(var n in e)x.isFunction(e[n])&&t.push(n);return t.sort()},x.extend=function(e){return T(u.call(arguments,1),function(t){if(t)for(var n in t)e[n]=t[n]}),e},x.pick=function(e){var t={},n=a.apply(r,u.call(arguments,1));return T(n,function(n){n in e&&(t[n]=e[n])}),t},x.omit=function(e){var t={},n=a.apply(r,u.call(arguments,1));for(var i in e)x.contains(n,i)||(t[i]=e[i]);return t},x.defaults=function(e){return T(u.call(arguments,1),function(t){if(t)for(var n in t)null==e[n]&&(e[n]=t[n])}),e},x.clone=function(e){return x.isObject(e)?x.isArray(e)?e.slice():x.extend({},e):e},x.tap=function(e,t){return t(e),e};var O=function(e,t,n,r){if(e===t)return 0!==e||1/e==1/t;if(null==e||null==t)return e===t;e instanceof x&&(e=e._wrapped),t instanceof x&&(t=t._wrapped);var i=f.call(e);if(i!=f.call(t))return!1;switch(i){case"[object String]":return e==t+"";case"[object Number]":return e!=+e?t!=+t:0==e?1/e==1/t:e==+t;case"[object Date]":case"[object Boolean]":return+e==+t;case"[object RegExp]":return e.source==t.source&&e.global==t.global&&e.multiline==t.multiline&&e.ignoreCase==t.ignoreCase}if("object"!=typeof e||"object"!=typeof t)return!1;for(var s=n.length;s--;)if(n[s]==e)return r[s]==t;n.push(e),r.push(t);var o=0,u=!0;if("[object Array]"==i){if(o=e.length,u=o==t.length)for(;o--&&(u=O(e[o],t[o],n,r)););}else{var a=e.constructor,l=t.constructor;if(a!==l&&!(x.isFunction(a)&&a instanceof a&&x.isFunction(l)&&l instanceof l))return!1;for(var c in e)if(x.has(e,c)&&(o++,!(u=x.has(t,c)&&O(e[c],t[c],n,r))))break;if(u){for(c in t)if(x.has(t,c)&&!(o--))break;u=!o}}return n.pop(),r.pop(),u};x.isEqual=function(e,t){return O(e,t,[],[])},x.isEmpty=function(e){if(null==e)return!0;if(x.isArray(e)||x.isString(e))return 0===e.length;for(var t in e)if(x.has(e,t))return!1;return!0},x.isElement=function(e){return!!e&&1===e.nodeType},x.isArray=w||function(e){return"[object Array]"==f.call(e)},x.isObject=function(e){return e===Object(e)},T(["Arguments","Function","String","Number","Date","RegExp"],function(e){x["is"+e]=function(t){return f.call(t)=="[object "+e+"]"}}),x.isArguments(arguments)||(x.isArguments=function(e){return!!e&&!!x.has(e,"callee")}),"function"!=typeof /./&&(x.isFunction=function(e){return"function"==typeof e}),x.isFinite=function(e){return isFinite(e)&&!isNaN(parseFloat(e))},x.isNaN=function(e){return x.isNumber(e)&&e!=+e},x.isBoolean=function(e){return e===!0||e===!1||"[object Boolean]"==f.call(e)},x.isNull=function(e){return null===e},x.isUndefined=function(e){return e===void 0},x.has=function(e,t){return l.call(e,t)},x.noConflict=function(){return e._=t,this},x.identity=function(e){return e},x.times=function(e,t,n){for(var r=Array(e),i=0;e>i;i++)r[i]=t.call(n,i);return r},x.random=function(e,t){return null==t&&(t=e,e=0),e+Math.floor(Math.random()*(t-e+1))};var M={escape:{"&":"&","<":"<",">":">",'"':""","'":"'","/":"/"}};M.unescape=x.invert(M.escape);var _={escape:RegExp("["+x.keys(M.escape).join("")+"]","g"),unescape:RegExp("("+x.keys(M.unescape).join("|")+")","g")};x.each(["escape","unescape"],function(e){x[e]=function(t){return null==t?"":(""+t).replace(_[e],function(t){return M[e][t]})}}),x.result=function(e,t){if(null==e)return null;var n=e[t];return x.isFunction(n)?n.call(e):n},x.mixin=function(e){T(x.functions(e),function(t){var n=x[t]=e[t];x.prototype[t]=function(){var e=[this._wrapped];return o.apply(e,arguments),j.call(this,n.apply(x,e))}})};var D=0;x.uniqueId=function(e){var t=++D+"";return e?e+t:t},x.templateSettings={evaluate:/<%([\s\S]+?)%>/g,interpolate:/<%=([\s\S]+?)%>/g,escape:/<%-([\s\S]+?)%>/g};var P=/(.)^/,H={"'":"'","\\":"\\","\r":"r","\n":"n"," ":"t","\u2028":"u2028","\u2029":"u2029"},B=/\\|'|\r|\n|\t|\u2028|\u2029/g;x.template=function(e,t,n){var r;n=x.defaults({},n,x.templateSettings);var i=RegExp([(n.escape||P).source,(n.interpolate||P).source,(n.evaluate||P).source].join("|")+"|$","g"),s=0,o="__p+='";e.replace(i,function(t,n,r,i,u){return o+=e.slice(s,u).replace(B,function(e){return"\\"+H[e]}),n&&(o+="'+\n((__t=("+n+"))==null?'':_.escape(__t))+\n'"),r&&(o+="'+\n((__t=("+r+"))==null?'':__t)+\n'"),i&&(o+="';\n"+i+"\n__p+='"),s=u+t.length,t}),o+="';\n",n.variable||(o="with(obj||{}){\n"+o+"}\n"),o="var __t,__p='',__j=Array.prototype.join,print=function(){__p+=__j.call(arguments,'');};\n"+o+"return __p;\n";try{r=Function(n.variable||"obj","_",o)}catch(u){throw u.source=o,u}if(t)return r(t,x);var a=function(e){return r.call(this,e,x)};return a.source="function("+(n.variable||"obj")+"){\n"+o+"}",a},x.chain=function(e){return x(e).chain()};var j=function(e){return this._chain?x(e).chain():e};x.mixin(x),T(["pop","push","reverse","shift","sort","splice","unshift"],function(e){var t=r[e];x.prototype[e]=function(){var n=this._wrapped;return t.apply(n,arguments),"shift"!=e&&"splice"!=e||0!==n.length||delete n[0],j.call(this,n)}}),T(["concat","join","slice"],function(e){var t=r[e];x.prototype[e]=function(){return j.call(this,t.apply(this._wrapped,arguments))}}),x.extend(x.prototype,{chain:function(){return this._chain=!0,this},value:function(){return this._wrapped}})}.call(this),typeof JSON!="object"&&(JSON={}),function(){"use strict";function f(e){return e<10?"0"+e:e}function quote(e){return escapable.lastIndex=0,escapable.test(e)?'"'+e.replace(escapable,function(e){var t=meta[e];return typeof t=="string"?t:"\\u"+("0000"+e.charCodeAt(0).toString(16)).slice(-4)})+'"':'"'+e+'"'}function str(e,t){var n,r,i,s,o=gap,u,a=t[e];a&&typeof a=="object"&&typeof a.toJSON=="function"&&(a=a.toJSON(e)),typeof rep=="function"&&(a=rep.call(t,e,a));switch(typeof a){case"string":return quote(a);case"number":return isFinite(a)?String(a):"null";case"boolean":case"null":return String(a);case"object":if(!a)return"null";gap+=indent,u=[];if(Object.prototype.toString.apply(a)==="[object Array]"){s=a.length;for(n=0;n').hide().appendTo("body")[0].contentWindow,this.navigate(t)),this._hasPushState?o(window).bind("popstate",this.checkUrl):this._wantsHashChange&&"onhashchange"in window&&!r?o(window).bind("hashchange",this.checkUrl):this._wantsHashChange&&(this._checkUrlInterval=setInterval(this.checkUrl,this.interval)),this.fragment=t;var i=window.location,u=i.pathname==this.options.root;if(this._wantsHashChange&&this._wantsPushState&&!this._hasPushState&&!u)return this.fragment=this.getFragment(null,!0),window.location.replace(this.options.root+"#"+this.fragment),!0;this._wantsPushState&&this._hasPushState&&u&&i.hash&&(this.fragment=this.getHash().replace(g,""),window.history.replaceState({},document.title,i.protocol+"//"+i.host+this.options.root+this.fragment));if(!this.options.silent)return this.loadUrl()},stop:function(){o(window).unbind("popstate",this.checkUrl).unbind("hashchange",this.checkUrl),clearInterval(this._checkUrlInterval),m.started=!1},route:function(e,t){this.handlers.unshift({route:e,callback:t})},checkUrl:function(e){var t=this.getFragment();t==this.fragment&&this.iframe&&(t=this.getFragment(this.getHash(this.iframe)));if(t==this.fragment)return!1;this.iframe&&this.navigate(t),this.loadUrl()||this.loadUrl(this.getHash())},loadUrl:function(e){var t=this.fragment=this.getFragment(e),n=s.any(this.handlers,function(e){if(e.route.test(t))return e.callback(t),!0});return n},navigate:function(e,t){if(!m.started)return!1;if(!t||t===!0)t={trigger:t};var n=(e||"").replace(g,"");if(this.fragment==n)return;this._hasPushState?(n.indexOf(this.options.root)!=0&&(n=this.options.root+n),this.fragment=n,window.history[t.replace?"replaceState":"pushState"]({},document.title,n)):this._wantsHashChange?(this.fragment=n,this._updateHash(window.location,n,t.replace),this.iframe&&n!=this.getFragment(this.getHash(this.iframe))&&(t.replace||this.iframe.document.open().close(),this._updateHash(this.iframe.location,n,t.replace))):window.location.assign(this.options.root+e),t.trigger&&this.loadUrl(e)},_updateHash:function(e,t,n){n?e.replace(e.toString().replace(/(javascript:|#).*$/,"")+"#"+t):e.hash=t}});var b=i.View=function(e){this.cid=s.uniqueId("view"),this._configure(e||{}),this._ensureElement(),this.initialize.apply(this,arguments),this.delegateEvents()},w=/^(\S+)\s*(.*)$/,E=["model","collection","el","id","attributes","className","tagName"];s.extend(b.prototype,a,{tagName:"div",$:function(e){return this.$el.find(e)},initialize:function(){},render:function(){return this},remove:function(){return this.$el.remove(),this},make:function(e,t,n){var r=document.createElement(e);return t&&o(r).attr(t),n&&o(r).html(n),r},setElement:function(e,t){return this.$el&&this.undelegateEvents(),this.$el=e instanceof o?e:o(e),this.el=this.$el[0],t!==!1&&this.delegateEvents(),this},delegateEvents:function(e){if(!e&&!(e=C(this,"events")))return;this.undelegateEvents();for(var t in e){var n=e[t];s.isFunction(n)||(n=this[e[t]]);if(!n)throw new Error('Method "'+e[t]+'" does not exist');var r=t.match(w),i=r[1],o=r[2];n=s.bind(n,this),i+=".delegateEvents"+this.cid,o===""?this.$el.bind(i,n):this.$el.delegate(o,i,n)}},undelegateEvents:function(){this.$el.unbind(".delegateEvents"+this.cid)},_configure:function(e){this.options&&(e=s.extend({},this.options,e));for(var t=0,n=E.length;t2?Array.prototype.slice.call(arguments,2):null;return function(){return e.apply(t,n||arguments)}},stamp:function(){var e=0,t="_leaflet_id";return function(n){return n[t]=n[t]||++e,n[t]}}(),invokeEach:function(e,t,n){var r,i;if(typeof e=="object"){i=Array.prototype.slice.call(arguments,3);for(r in e)t.apply(n,[r,e[r]].concat(i));return!0}return!1},limitExecByInterval:function(e,t,n){var r,i;return function s(){var o=arguments;if(r){i=!0;return}r=!0,setTimeout(function(){r=!1,i&&(s.apply(n,o),i=!1)},t),e.apply(n,o)}},falseFn:function(){return!1},formatNum:function(e,t){var n=Math.pow(10,t||5);return Math.round(e*n)/n},trim:function(e){return e.trim?e.trim():e.replace(/^\s+|\s+$/g,"")},splitWords:function(e){return i.Util.trim(e).split(/\s+/)},setOptions:function(e,t){return e.options=i.extend({},e.options,t),e.options},getParamString:function(e,t,n){var r=[];for(var i in e)r.push(encodeURIComponent(n?i.toUpperCase():i)+"="+encodeURIComponent(e[i]));return(!t||t.indexOf("?")===-1?"?":"&")+r.join("&")},template:function(e,t){return e.replace(/\{ *([\w_]+) *\}/g,function(e,r){var i=t[r];if(i===n)throw new Error("No value provided for variable "+e);return typeof i=="function"&&(i=i(t)),i})},isArray:Array.isArray||function(e){return Object.prototype.toString.call(e)==="[object Array]"},emptyImageUrl:"data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs="},function(){function t(t){var n,r,i=["webkit","moz","o","ms"];for(n=0;n0||e+"_idx"in t&&t[e+"_idx_len"]>0)},removeEventListener:function(e,t,n){if(!this[s])return this;if(!e)return this.clearAllEventListeners();if(i.Util.invokeEach(e,this.removeEventListener,this,t,n))return this;var r=this[s],o=n&&n!==this&&i.stamp(n),u,a,f,l,c,h,p,d,v;e=i.Util.splitWords(e);for(u=0,a=e.length;u=0;c--)l[c].action===t&&(!n||l[c].context===n)&&(v=l.splice(c,1),v[0].action=i.Util.falseFn);n&&d&&l.length===0&&(delete d[o],r[p]--)}}}return this},clearAllEventListeners:function(){return delete this[s],this},fireEvent:function(e,t){if(!this.hasEventListeners(e))return this;var n=i.Util.extend({},t,{type:e,target:this}),r=this[s],o,u,a,f,l;if(r[e]){o=r[e].slice();for(u=0,a=o.length;u1||"matchMedia"in e&&e.matchMedia("(min-resolution:144dpi)")&&e.matchMedia("(min-resolution:144dpi)").matches,g=t.documentElement,y=r&&"transition"in g.style,b="WebKitCSSMatrix"in e&&"m11"in new e.WebKitCSSMatrix&&!c,w="MozPerspective"in g.style,E="OTransition"in g.style,S=!e.L_DISABLE_3D&&(y||b||w||E)&&!f,x=!e.L_NO_TOUCH&&!f&&function(){var e="ontouchstart";if(v||e in g)return!0;var n=t.createElement("div"),r=!1;return n.setAttribute?(n.setAttribute(e,"return;"),typeof n[e]=="function"&&(r=!0),n.removeAttribute(e),n=null,r):!1}();i.Browser={ie:r,ielt9:s,webkit:u,gecko:h&&!u&&!e.opera&&!r,android:l,android23:c,chrome:a,ie3d:y,webkit3d:b,gecko3d:w,opera3d:E,any3d:S,mobile:p,mobileWebkit:p&&u,mobileWebkit3d:p&&b,mobileOpera:p&&e.opera,touch:x,msPointer:d,pointer:v,retina:m}}(),i.Point=function(e,t,n){this.x=n?Math.round(e):e,this.y=n?Math.round(t):t},i.Point.prototype={clone:function(){return new i.Point(this.x,this.y)},add:function(e){return this.clone()._add(i.point(e))},_add:function(e){return this.x+=e.x,this.y+=e.y,this},subtract:function(e){return this.clone()._subtract(i.point(e))},_subtract:function(e){return this.x-=e.x,this.y-=e.y,this},divideBy:function(e){return this.clone()._divideBy(e)},_divideBy:function(e){return this.x/=e,this.y/=e,this},multiplyBy:function(e){return this.clone()._multiplyBy(e)},_multiplyBy:function(e){return this.x*=e,this.y*=e,this},round:function(){return this.clone()._round()},_round:function(){return this.x=Math.round(this.x),this.y=Math.round(this.y),this},floor:function(){return this.clone()._floor()},_floor:function(){return this.x=Math.floor(this.x),this.y=Math.floor(this.y),this},distanceTo:function(e){e=i.point(e);var t=e.x-this.x,n=e.y-this.y;return Math.sqrt(t*t+n*n)},equals:function(e){return e=i.point(e),e.x===this.x&&e.y===this.y},contains:function(e){return e=i.point(e),Math.abs(e.x)<=Math.abs(this.x)&&Math.abs(e.y)<=Math.abs(this.y)},toString:function(){return"Point("+i.Util.formatNum(this.x)+", "+i.Util.formatNum(this.y)+")"}},i.point=function(e,t,r){return e instanceof i.Point?e:i.Util.isArray(e)?new i.Point(e[0],e[1]):e===n||e===null?e:new i.Point(e,t,r)},i.Bounds=function(e,t){if(!e)return;var n=t?[e,t]:e;for(var r=0,i=n.length;r=this.min.x&&n.x<=this.max.x&&t.y>=this.min.y&&n.y<=this.max.y},intersects:function(e){e=i.bounds(e);var t=this.min,n=this.max,r=e.min,s=e.max,o=s.x>=t.x&&r.x<=n.x,u=s.y>=t.y&&r.y<=n.y;return o&&u},isValid:function(){return!!this.min&&!!this.max}},i.bounds=function(e,t){return!e||e instanceof i.Bounds?e:new i.Bounds(e,t)},i.Transformation=function(e,t,n,r){this._a=e,this._b=t,this._c=n,this._d=r},i.Transformation.prototype={transform:function(e,t){return this._transform(e.clone(),t)},_transform:function(e,t){return t=t||1,e.x=t*(this._a*e.x+this._b),e.y=t*(this._c*e.y+this._d),e},untransform:function(e,t){return t=t||1,new i.Point((e.x/t-this._b)/this._a,(e.y/t-this._d)/this._c)}},i.DomUtil={get:function(e){return typeof e=="string"?t.getElementById(e):e},getStyle:function(e,n){var r=e.style[n];!r&&e.currentStyle&&(r=e.currentStyle[n]);if((!r||r==="auto")&&t.defaultView){var i=t.defaultView.getComputedStyle(e,null);r=i?i[n]:null}return r==="auto"?null:r},getViewportOffset:function(e){var n=0,r=0,s=e,o=t.body,u=t.documentElement,a;do{n+=s.offsetTop||0,r+=s.offsetLeft||0,n+=parseInt(i.DomUtil.getStyle(s,"borderTopWidth"),10)||0,r+=parseInt(i.DomUtil.getStyle(s,"borderLeftWidth"),10)||0,a=i.DomUtil.getStyle(s,"position");if(s.offsetParent===o&&a==="absolute")break;if(a==="fixed"){n+=o.scrollTop||u.scrollTop||0,r+=o.scrollLeft||u.scrollLeft||0;break}if(a==="relative"&&!s.offsetLeft){var f=i.DomUtil.getStyle(s,"width"),l=i.DomUtil.getStyle(s,"max-width"),c=s.getBoundingClientRect();if(f!=="none"||l!=="none")r+=c.left+s.clientLeft;n+=c.top+(o.scrollTop||u.scrollTop||0);break}s=s.offsetParent}while(s);s=e;do{if(s===o)break;n-=s.scrollTop||0,r-=s.scrollLeft||0,s=s.parentNode}while(s);return new i.Point(r,n)},documentIsLtr:function(){return i.DomUtil._docIsLtrCached||(i.DomUtil._docIsLtrCached=!0,i.DomUtil._docIsLtr=i.DomUtil.getStyle(t.body,"direction")==="ltr"),i.DomUtil._docIsLtr},create:function(e,n,r){var i=t.createElement(e);return i.className=n,r&&r.appendChild(i),i},hasClass:function(e,t){if(e.classList!==n)return e.classList.contains(t);var r=i.DomUtil._getClass(e);return r.length>0&&(new RegExp("(^|\\s)"+t+"(\\s|$)")).test(r)},addClass:function(e,t){if(e.classList!==n){var r=i.Util.splitWords(t);for(var s=0,o=r.length;s=t.lat&&s.lat<=n.lat&&r.lng>=t.lng&&s.lng<=n.lng},intersects:function(e){e=i.latLngBounds(e);var t=this._southWest,n=this._northEast,r=e.getSouthWest(),s=e.getNorthEast(),o=s.lat>=t.lat&&r.lat<=n.lat,u=s.lng>=t.lng&&r.lng<=n.lng;return o&&u},toBBoxString:function(){return[this.getWest(),this.getSouth(),this.getEast(),this.getNorth()].join(",")},equals:function(e){return e?(e=i.latLngBounds(e),this._southWest.equals(e.getSouthWest())&&this._northEast.equals(e.getNorthEast())):!1},isValid:function(){return!!this._southWest&&!!this._northEast}},i.latLngBounds=function(e,t){return!e||e instanceof i.LatLngBounds?e:new i.LatLngBounds(e,t)},i.Projection={},i.Projection.SphericalMercator={MAX_LATITUDE:85.0511287798,project:function(e){var t=i.LatLng.DEG_TO_RAD,n=this.MAX_LATITUDE,r=Math.max(Math.min(n,e.lat),-n),s=e.lng*t,o=r*t;return o=Math.log(Math.tan(Math.PI/4+o/2)),new i.Point(s,o)},unproject:function(e){var t=i.LatLng.RAD_TO_DEG,n=e.x*t,r=(2*Math.atan(Math.exp(e.y))-Math.PI/2)*t;return new i.LatLng(r,n)}},i.Projection.LonLat={project:function(e){return new i.Point(e.lng,e.lat)},unproject:function(e){return new i.LatLng(e.y,e.x)}},i.CRS={latLngToPoint:function(e,t){var n=this.projection.project(e),r=this.scale(t);return this.transformation._transform(n,r)},pointToLatLng:function(e,t){var n=this.scale(t),r=this.transformation.untransform(e,n);return this.projection.unproject(r)},project:function(e){return this.projection.project(e)},scale:function(e){return 256*Math.pow(2,e)},getSize:function(e){var t=this.scale(e);return i.point(t,t)}},i.CRS.Simple=i.extend({},i.CRS,{projection:i.Projection.LonLat,transformation:new i.Transformation(1,0,-1,0),scale:function(e){return Math.pow(2,e)}}),i.CRS.EPSG3857=i.extend({},i.CRS,{code:"EPSG:3857",projection:i.Projection.SphericalMercator,transformation:new i.Transformation(.5/Math.PI,.5,-0.5/Math.PI,.5),project:function(e){var t=this.projection.project(e),n=6378137;return t.multiplyBy(n)}}),i.CRS.EPSG900913=i.extend({},i.CRS.EPSG3857,{code:"EPSG:900913"}),i.CRS.EPSG4326=i.extend({},i.CRS,{code:"EPSG:4326",projection:i.Projection.LonLat,transformation:new i.Transformation(1/360,.5,-1/360,.5)}),i.Map=i.Class.extend({includes:i.Mixin.Events,options:{crs:i.CRS.EPSG3857,fadeAnimation:i.DomUtil.TRANSITION&&!i.Browser.android23,trackResize:!0,markerZoomAnimation:i.DomUtil.TRANSITION&&i.Browser.any3d},initialize:function(e,t){t=i.setOptions(this,t),this._initContainer(e),this._initLayout(),this._onResize=i.bind(this._onResize,this),this._initEvents(),t.maxBounds&&this.setMaxBounds(t.maxBounds),t.center&&t.zoom!==n&&this.setView(i.latLng(t.center),t.zoom,{reset:!0}),this._handlers=[],this._layers={},this._zoomBoundLayers={},this._tileLayersNum=0,this.callInitHooks(),this._addLayers(t.layers)},setView:function(e,t){return t=t===n?this.getZoom():t,this._resetView(i.latLng(e),this._limitZoom(t)),this},setZoom:function(e,t){return this._loaded?this.setView(this.getCenter(),e,{zoom:t}):(this._zoom=this._limitZoom(e),this)},zoomIn:function(e,t){return this.setZoom(this._zoom+(e||1),t)},zoomOut:function(e,t){return this.setZoom(this._zoom-(e||1),t)},setZoomAround:function(e,t,n){var r=this.getZoomScale(t),s=this.getSize().divideBy(2),o=e instanceof i.Point?e:this.latLngToContainerPoint(e),u=o.subtract(s).multiplyBy(1-1/r),a=this.containerPointToLatLng(s.add(u));return this.setView(a,t,{zoom:n})},fitBounds:function(e,t){t=t||{},e=e.getBounds?e.getBounds():i.latLngBounds(e);var n=i.point(t.paddingTopLeft||t.padding||[0,0]),r=i.point(t.paddingBottomRight||t.padding||[0,0]),s=this.getBoundsZoom(e,!1,n.add(r)),o=r.subtract(n).divideBy(2),u=this.project(e.getSouthWest(),s),a=this.project(e.getNorthEast(),s),f=this.unproject(u.add(a).divideBy(2).add(o),s);return s=t&&t.maxZoom?Math.min(t.maxZoom,s):s,this.setView(f,s,t)},fitWorld:function(e){return this.fitBounds([[-90,-180],[90,180]],e)},panTo:function(e,t){return this.setView(e,this._zoom,{pan:t})},panBy:function(e){return this.fire("movestart"),this._rawPanBy(i.point(e)),this.fire("move"),this.fire("moveend")},setMaxBounds:function(e){return e=i.latLngBounds(e),this.options.maxBounds=e,e?(this._loaded&&this._panInsideMaxBounds(),this.on("moveend",this._panInsideMaxBounds,this)):this.off("moveend",this._panInsideMaxBounds,this)},panInsideBounds:function(e,t){var n=this.getCenter(),r=this._limitCenter(n,this._zoom,e);return n.equals(r)?this:this.panTo(r,t)},addLayer:function(e){var t=i.stamp(e);return this._layers[t]?this:(this._layers[t]=e,e.options&&(!isNaN(e.options.maxZoom)||!isNaN(e.options.minZoom))&&(this._zoomBoundLayers[t]=e,this._updateZoomLevels()),this.options.zoomAnimation&&i.TileLayer&&e instanceof i.TileLayer&&(this._tileLayersNum++,this._tileLayersToLoad++,e.on("load",this._onTileLayerLoad,this)),this._loaded&&this._layerAdd(e),this)},removeLayer:function(e){var t=i.stamp(e);return this._layers[t]?(this._loaded&&e.onRemove(this),delete this._layers[t],this._loaded&&this.fire("layerremove",{layer:e}),this._zoomBoundLayers[t]&&(delete this._zoomBoundLayers[t],this._updateZoomLevels()),this.options.zoomAnimation&&i.TileLayer&&e instanceof i.TileLayer&&(this._tileLayersNum--,this._tileLayersToLoad--,e.off("load",this._onTileLayerLoad,this)),this):this},hasLayer:function(e){return e?i.stamp(e)in this._layers:!1},eachLayer:function(e,t){for(var n in this._layers)e.call(t,this._layers[n]);return this},invalidateSize:function(e){if(!this._loaded)return this;e=i.extend({animate:!1,pan:!0},e===!0?{animate:!0}:e);var t=this.getSize();this._sizeChanged=!0,this._initialCenter=null;var n=this.getSize(),r=t.divideBy(2).round(),s=n.divideBy(2).round(),o=r.subtract(s);return!o.x&&!o.y?this:(e.animate&&e.pan?this.panBy(o):(e.pan&&this._rawPanBy(o),this.fire("move"),e.debounceMoveend?(clearTimeout(this._sizeTimer),this._sizeTimer=setTimeout(i.bind(this.fire,this,"moveend"),200)):this.fire("moveend")),this.fire("resize",{oldSize:t,newSize:n}))},addHandler:function(e,t){if(!t)return this;var n=this[e]=new t(this);return this._handlers.push(n),this.options[e]&&n.enable(),this},remove:function(){this._loaded&&this.fire("unload"),this._initEvents("off");try{delete this._container._leaflet}catch(e){this._container._leaflet=n}return this._clearPanes(),this._clearControlPos&&this._clearControlPos(),this._clearHandlers(),this},getCenter:function(){return this._checkIfLoaded(),this._initialCenter&&!this._moved()?this._initialCenter:this.layerPointToLatLng(this._getCenterLayerPoint())},getZoom:function(){return this._zoom},getBounds:function(){var e=this.getPixelBounds(),t=this.unproject(e.getBottomLeft()),n=this.unproject(e.getTopRight());return new i.LatLngBounds(t,n)},getMinZoom:function(){return this.options.minZoom===n?this._layersMinZoom===n?0:this._layersMinZoom:this.options.minZoom},getMaxZoom:function(){return this.options.maxZoom===n?this._layersMaxZoom===n?Infinity:this._layersMaxZoom:this.options.maxZoom},getBoundsZoom:function(e,t,n){e=i.latLngBounds(e);var r=this.getMinZoom()-(t?1:0),s=this.getMaxZoom(),o=this.getSize(),u=e.getNorthWest(),a=e.getSouthEast(),f=!0,l;n=i.point(n||[0,0]);do r++,l=this.project(a,r).subtract(this.project(u,r)).add(n),f=t?l.x0?Math.round(e-t)/2:Math.max(0,Math.ceil(e))-Math.max(0,Math.floor(t))},_limitZoom:function(e){var t=this.getMinZoom(),n=this.getMaxZoom();return Math.max(t,Math.min(n,e))}}),i.map=function(e,t){return new i.Map(e,t)},i.Projection.Mercator={MAX_LATITUDE:85.0840591556,R_MINOR:6356752.314245179,R_MAJOR:6378137,project:function(e){var t=i.LatLng.DEG_TO_RAD,n=this.MAX_LATITUDE,r=Math.max(Math.min(n,e.lat),-n),s=this.R_MAJOR,o=this.R_MINOR,u=e.lng*t*s,a=r*t,f=o/s,l=Math.sqrt(1-f*f),c=l*Math.sin(a);c=Math.pow((1-c)/(1+c),l*.5);var h=Math.tan(.5*(Math.PI*.5-a))/c;return a=-s*Math.log(h),new i.Point(u,a)},unproject:function(e){var t=i.LatLng.RAD_TO_DEG,n=this.R_MAJOR,r=this.R_MINOR,s=e.x*t/n,o=r/n,u=Math.sqrt(1-o*o),a=Math.exp(-e.y/n),f=Math.PI/2-2*Math.atan(a),l=15,c=1e-7,h=l,p=.1,d;while(Math.abs(p)>c&&--h>0)d=u*Math.sin(f),p=Math.PI/2-2*Math.atan(a*Math.pow((1-d)/(1+d),.5*u))-f,f+=p;return new i.LatLng(f*t,s)}},i.CRS.EPSG3395=i.extend({},i.CRS,{code:"EPSG:3395",projection:i.Projection.Mercator,transformation:function(){var e=i.Projection.Mercator,t=e.R_MAJOR,n=.5/(Math.PI*t);return new i.Transformation(n,.5,-n,.5)}()}),i.TileLayer=i.Class.extend({includes:i.Mixin.Events,options:{minZoom:0,maxZoom:18,tileSize:256,subdomains:"abc",errorTileUrl:"",attribution:"",zoomOffset:0,opacity:1,unloadInvisibleTiles:i.Browser.mobile,updateWhenIdle:i.Browser.mobile},initialize:function(e,t){t=i.setOptions(this,t),t.detectRetina&&i.Browser.retina&&t.maxZoom>0&&(t.tileSize=Math.floor(t.tileSize/2),t.zoomOffset++,t.minZoom>0&&t.minZoom--,this.options.maxZoom--),t.bounds&&(t.bounds=i.latLngBounds(t.bounds)),this._url=e;var n=this.options.subdomains;typeof n=="string"&&(this.options.subdomains=n.split(""))},onAdd:function(e){this._map=e,this._animated=e._zoomAnimated,this._initContainer(),e.on({viewreset:this._reset,moveend:this._update},this),this._animated&&e.on({zoomanim:this._animateZoom,zoomend:this._endZoomAnim},this),this.options.updateWhenIdle||(this._limitedUpdate=i.Util.limitExecByInterval(this._update,150,this),e.on("move",this._limitedUpdate,this)),this._reset(),this._update()},addTo:function(e){return e.addLayer(this),this},onRemove:function(e){this._container.parentNode.removeChild(this._container),e.off({viewreset:this._reset,moveend:this._update},this),this._animated&&e.off({zoomanim:this._animateZoom,zoomend:this._endZoomAnim},this),this.options.updateWhenIdle||e.off("move",this._limitedUpdate,this),this._container=null,this._map=null},bringToFront:function(){var e=this._map._panes.tilePane;return this._container&&(e.appendChild(this._container),this._setAutoZIndex(e,Math.max)),this},bringToBack:function(){var e=this._map._panes.tilePane;return this._container&&(e.insertBefore(this._container,e.firstChild),this._setAutoZIndex(e,Math.min)),this},getAttribution:function(){return this.options.attribution},getContainer:function(){return this._container},setOpacity:function(e){return this.options.opacity=e,this._map&&this._updateOpacity(),this},setZIndex:function(e){return this.options.zIndex=e,this._updateZIndex(),this},setUrl:function(e,t){return this._url=e,t||this.redraw(),this},redraw:function(){return this._map&&(this._reset({hard:!0}),this._update()),this},_updateZIndex:function(){this._container&&this.options.zIndex!==n&&(this._container.style.zIndex=this.options.zIndex)},_setAutoZIndex:function(e,t){var n=e.children,r=-t(Infinity,-Infinity),i,s,o;for(s=0,o=n.length;sn&&(r=Math.round(e.getZoomScale(t)/e.getZoomScale(n)*r)),r},_update:function(){if(!this._map)return;var e=this._map,t=e.getPixelBounds(),n=e.getZoom(),r=this._getTileSize( +);if(n>this.options.maxZoom||n=n.x)||e.y<0||e.y>=n.y)return!1}if(t.bounds){var r=t.tileSize,i=e.multiplyBy(r),s=i.add([r,r]),o=this._map.unproject(i),u=this._map.unproject(s);!t.continuousWorld&&!t.noWrap&&(o=o.wrap(),u=u.wrap());if(!t.bounds.intersects([o,u]))return!1}return!0},_removeOtherTiles:function(e){var t,n,r,i;for(i in this._tiles)t=i.split(":"),n=parseInt(t[0],10),r=parseInt(t[1],10),(ne.max.x||re.max.y)&&this._removeTile(i)},_removeTile:function(e){var t=this._tiles[e];this.fire("tileunload",{tile:t,url:t.src}),this.options.reuseTiles?(i.DomUtil.removeClass(t,"leaflet-tile-loaded"),this._unusedTiles.push(t)):t.parentNode===this._tileContainer&&this._tileContainer.removeChild(t),i.Browser.android||(t.onload=null,t.src=i.Util.emptyImageUrl),delete this._tiles[e]},_addTile:function(e,t){var n=this._getTilePos(e),r=this._getTile();i.DomUtil.setPosition(r,n,i.Browser.chrome),this._tiles[e.x+":"+e.y]=r,this._loadTile(r,e),r.parentNode!==this._tileContainer&&t.appendChild(r)},_getZoomForUrl:function(){var e=this.options,t=this._map.getZoom();return e.zoomReverse&&(t=e.maxZoom-t),t+=e.zoomOffset,e.maxNativeZoom?Math.min(t,e.maxNativeZoom):t},_getTilePos:function(e){var t=this._map.getPixelOrigin(),n=this._getTileSize();return e.multiplyBy(n).subtract(t)},getTileUrl:function(e){return i.Util.template(this._url,i.extend({s:this._getSubdomain(e),z:e.z,x:e.x,y:e.y},this.options))},_getWrapTileNum:function(){var e=this._map.options.crs,t=e.getSize(this._map.getZoom());return t.divideBy(this._getTileSize())._floor()},_adjustTilePoint:function(e){var t=this._getWrapTileNum();!this.options.continuousWorld&&!this.options.noWrap&&(e.x=(e.x%t.x+t.x)%t.x),this.options.tms&&(e.y=t.y-e.y-1),e.z=this._getZoomForUrl()},_getSubdomain:function(e){var t=Math.abs(e.x+e.y)%this.options.subdomains.length;return this.options.subdomains[t]},_getTile:function(){if(this.options.reuseTiles&&this._unusedTiles.length>0){var e=this._unusedTiles.pop();return this._resetTile(e),e}return this._createTile()},_resetTile:function(){},_createTile:function(){var e=i.DomUtil.create("img","leaflet-tile");return e.style.width=e.style.height=this._getTileSize()+"px",e.galleryimg="no",e.onselectstart=e.onmousemove=i.Util.falseFn,i.Browser.ielt9&&this.options.opacity!==n&&i.DomUtil.setOpacity(e,this.options.opacity),i.Browser.mobileWebkit3d&&(e.style.WebkitBackfaceVisibility="hidden"),e},_loadTile:function(e,t){e._layer=this,e.onload=this._tileOnLoad,e.onerror=this._tileOnError,this._adjustTilePoint(t),e.src=this.getTileUrl(t),this.fire("tileloadstart",{tile:e,url:e.src})},_tileLoaded:function(){this._tilesToLoad--,this._animated&&i.DomUtil.addClass(this._tileContainer,"leaflet-zoom-animated"),this._tilesToLoad||(this.fire("load"),this._animated&&(clearTimeout(this._clearBgBufferTimer),this._clearBgBufferTimer=setTimeout(i.bind(this._clearBgBuffer,this),500)))},_tileOnLoad:function(){var e=this._layer;this.src!==i.Util.emptyImageUrl&&(i.DomUtil.addClass(this,"leaflet-tile-loaded"),e.fire("tileload",{tile:this,url:this.src})),e._tileLoaded()},_tileOnError:function(){var e=this._layer;e.fire("tileerror",{tile:this,url:this.src});var t=e.options.errorTileUrl;t&&(this.src=t),e._tileLoaded()}}),i.tileLayer=function(e,t){return new i.TileLayer(e,t)},i.TileLayer.WMS=i.TileLayer.extend({defaultWmsParams:{service:"WMS",request:"GetMap",version:"1.1.1",layers:"",styles:"",format:"image/jpeg",transparent:!1},initialize:function(e,t){this._url=e;var n=i.extend({},this.defaultWmsParams),r=t.tileSize||this.options.tileSize;t.detectRetina&&i.Browser.retina?n.width=n.height=r*2:n.width=n.height=r;for(var s in t)!this.options.hasOwnProperty(s)&&s!=="crs"&&(n[s]=t[s]);this.wmsParams=n,i.setOptions(this,t)},onAdd:function(e){this._crs=this.options.crs||e.options.crs,this._wmsVersion=parseFloat(this.wmsParams.version);var t=this._wmsVersion>=1.3?"crs":"srs";this.wmsParams[t]=this._crs.code,i.TileLayer.prototype.onAdd.call(this,e)},getTileUrl:function(e){var t=this._map,n=this.options.tileSize,r=e.multiplyBy(n),s=r.add([n,n]),o=this._crs.project(t.unproject(r,e.z)),u=this._crs.project(t.unproject(s,e.z)),a=this._wmsVersion>=1.3&&this._crs===i.CRS.EPSG4326?[u.y,o.x,o.y,u.x].join(","):[o.x,u.y,u.x,o.y].join(","),f=i.Util.template(this._url,{s:this._getSubdomain(e)});return f+i.Util.getParamString(this.wmsParams,f,!0)+"&BBOX="+a},setParams:function(e,t){return i.extend(this.wmsParams,e),t||this.redraw(),this}}),i.tileLayer.wms=function(e,t){return new i.TileLayer.WMS(e,t)},i.TileLayer.Canvas=i.TileLayer.extend({options:{async:!1},initialize:function(e){i.setOptions(this,e)},redraw:function(){this._map&&(this._reset({hard:!0}),this._update());for(var e in this._tiles)this._redrawTile(this._tiles[e]);return this},_redrawTile:function(e){this.drawTile(e,e._tilePoint,this._map._zoom)},_createTile:function(){var e=i.DomUtil.create("canvas","leaflet-tile");return e.width=e.height=this.options.tileSize,e.onselectstart=e.onmousemove=i.Util.falseFn,e},_loadTile:function(e,t){e._layer=this,e._tilePoint=t,this._redrawTile(e),this.options.async||this.tileDrawn(e)},drawTile:function(){},tileDrawn:function(e){this._tileOnLoad.call(e)}}),i.tileLayer.canvas=function(e){return new i.TileLayer.Canvas(e)},i.ImageOverlay=i.Class.extend({includes:i.Mixin.Events,options:{opacity:1},initialize:function(e,t,n){this._url=e,this._bounds=i.latLngBounds(t),i.setOptions(this,n)},onAdd:function(e){this._map=e,this._image||this._initImage(),e._panes.overlayPane.appendChild(this._image),e.on("viewreset",this._reset,this),e.options.zoomAnimation&&i.Browser.any3d&&e.on("zoomanim",this._animateZoom,this),this._reset()},onRemove:function(e){e.getPanes().overlayPane.removeChild(this._image),e.off("viewreset",this._reset,this),e.options.zoomAnimation&&e.off("zoomanim",this._animateZoom,this)},addTo:function(e){return e.addLayer(this),this},setOpacity:function(e){return this.options.opacity=e,this._updateOpacity(),this},bringToFront:function(){return this._image&&this._map._panes.overlayPane.appendChild(this._image),this},bringToBack:function(){var e=this._map._panes.overlayPane;return this._image&&e.insertBefore(this._image,e.firstChild),this},setUrl:function(e){this._url=e,this._image.src=this._url},getAttribution:function(){return this.options.attribution},_initImage:function(){this._image=i.DomUtil.create("img","leaflet-image-layer"),this._map.options.zoomAnimation&&i.Browser.any3d?i.DomUtil.addClass(this._image,"leaflet-zoom-animated"):i.DomUtil.addClass(this._image,"leaflet-zoom-hide"),this._updateOpacity(),i.extend(this._image,{galleryimg:"no",onselectstart:i.Util.falseFn,onmousemove:i.Util.falseFn,onload:i.bind(this._onImageLoad,this),src:this._url})},_animateZoom:function(e){var t=this._map,n=this._image,r=t.getZoomScale(e.zoom),s=this._bounds.getNorthWest(),o=this._bounds.getSouthEast(),u=t._latLngToNewLayerPoint(s,e.zoom,e.center),a=t._latLngToNewLayerPoint(o,e.zoom,e.center)._subtract(u),f=u._add(a._multiplyBy(.5*(1-1/r)));n.style[i.DomUtil.TRANSFORM]=i.DomUtil.getTranslateString(f)+" scale("+r+") "},_reset:function(){var e=this._image,t=this._map.latLngToLayerPoint(this._bounds.getNorthWest()),n=this._map.latLngToLayerPoint(this._bounds.getSouthEast())._subtract(t);i.DomUtil.setPosition(e,t),e.style.width=n.x+"px",e.style.height=n.y+"px"},_onImageLoad:function(){this.fire("load")},_updateOpacity:function(){i.DomUtil.setOpacity(this._image,this.options.opacity)}}),i.imageOverlay=function(e,t,n){return new i.ImageOverlay(e,t,n)},i.Icon=i.Class.extend({options:{className:""},initialize:function(e){i.setOptions(this,e)},createIcon:function(e){return this._createIcon("icon",e)},createShadow:function(e){return this._createIcon("shadow",e)},_createIcon:function(e,t){var n=this._getIconUrl(e);if(!n){if(e==="icon")throw new Error("iconUrl not set in Icon options (see the docs).");return null}var r;return!t||t.tagName!=="IMG"?r=this._createImg(n):r=this._createImg(n,t),this._setIconStyles(r,e),r},_setIconStyles:function(e,t){var n=this.options,r=i.point(n[t+"Size"]),s;t==="shadow"?s=i.point(n.shadowAnchor||n.iconAnchor):s=i.point(n.iconAnchor),!s&&r&&(s=r.divideBy(2,!0)),e.className="leaflet-marker-"+t+" "+n.className,s&&(e.style.marginLeft=-s.x+"px",e.style.marginTop=-s.y+"px"),r&&(e.style.width=r.x+"px",e.style.height=r.y+"px")},_createImg:function(e,n){return n=n||t.createElement("img"),n.src=e,n},_getIconUrl:function(e){return i.Browser.retina&&this.options[e+"RetinaUrl"]?this.options[e+"RetinaUrl"]:this.options[e+"Url"]}}),i.icon=function(e){return new i.Icon(e)},i.Icon.Default=i.Icon.extend({options:{iconSize:[25,41],iconAnchor:[12,41],popupAnchor:[1,-34],shadowSize:[41,41]},_getIconUrl:function(e){var t=e+"Url";if(this.options[t])return this.options[t];i.Browser.retina&&e==="icon"&&(e+="-2x");var n=i.Icon.Default.imagePath;if(!n)throw new Error("Couldn't autodetect L.Icon.Default.imagePath, set it manually.");return n+"/marker-"+e+".png"}}),i.Icon.Default.imagePath=function(){var e=t.getElementsByTagName("script"),n=/[\/^]leaflet[\-\._]?([\w\-\._]*)\.js\??/,r,i,s,o,u;for(r=0,i=e.length;rs?(t.height=s+"px",i.DomUtil.addClass(e,o)):i.DomUtil.removeClass(e,o),this._containerWidth=this._container.offsetWidth},_updatePosition:function(){if(!this._map)return;var e=this._map.latLngToLayerPoint(this._latlng),t=this._animated,n=i.point(this.options.offset);t&&i.DomUtil.setPosition(this._container,e),this._containerBottom=-n.y-(t?0:e.y),this._containerLeft=-Math.round(this._containerWidth/2)+n.x+(t?0:e.x),this._container.style.bottom=this._containerBottom+"px",this._container.style.left=this._containerLeft+"px"},_zoomAnimation:function(e){var t=this._map._latLngToNewLayerPoint(this._latlng,e.zoom,e.center);i.DomUtil.setPosition(this._container,t)},_adjustPan:function(){if(!this.options.autoPan)return;var e=this._map,t=this._container.offsetHeight,n=this._containerWidth,r=new i.Point(this._containerLeft,-t-this._containerBottom);this._animated&&r._add(i.DomUtil.getPosition(this._container));var s=e.layerPointToContainerPoint(r),o=i.point(this.options.autoPanPadding),u=i.point(this.options.autoPanPaddingTopLeft||o),a=i.point(this.options.autoPanPaddingBottomRight||o),f=e.getSize(),l=0,c=0;s.x+n+a.x>f.x&&(l=s.x+n-f.x+a.x),s.x-l-u.x<0&&(l=s.x-u.x),s.y+t+a.y>f.y&&(c=s.y+t-f.y+a.y),s.y-c-u.y<0&&(c=s.y-u.y),(l||c)&&e.fire("autopanstart").panBy([l,c])},_onCloseButtonClick:function(e){this._close(),i.DomEvent.stop(e)}}),i.popup=function(e,t){return new i.Popup(e,t)},i.Map.include({openPopup:function(e,t,n){this.closePopup();if(!(e instanceof i.Popup)){var r=e;e=(new i.Popup(n)).setLatLng(t).setContent(r)}return e._isOpen=!0,this._popup=e,this.addLayer(e)},closePopup:function(e){if(!e||e===this._popup)e=this._popup,this._popup=null;return e&&(this.removeLayer(e),e._isOpen=!1),this}}),i.Marker.include({openPopup:function(){return this._popup&&this._map&&!this._map.hasLayer(this._popup)&&(this._popup.setLatLng(this._latlng),this._map.openPopup(this._popup)),this},closePopup:function(){return this._popup&&this._popup._close(),this},togglePopup:function(){return this._popup&&(this._popup._isOpen?this.closePopup():this.openPopup()),this},bindPopup:function(e,t){var n=i.point(this.options.icon.options.popupAnchor||[0,0]);return n=n.add(i.Popup.prototype.options.offset),t&&t.offset&&(n=n.add(t.offset)),t=i.extend({offset:n},t),this._popupHandlersAdded||(this.on("click",this.togglePopup,this).on("remove",this.closePopup,this).on("move",this._movePopup,this),this._popupHandlersAdded=!0),e instanceof i.Popup?(i.setOptions(e,t),this._popup=e):this._popup=(new i.Popup(t,this)).setContent(e),this},setPopupContent:function(e){return this._popup&&this._popup.setContent(e),this},unbindPopup:function(){return this._popup&&(this._popup=null,this.off("click",this.togglePopup,this).off("remove",this.closePopup,this).off("move",this._movePopup,this),this._popupHandlersAdded=!1),this},getPopup:function(){return this._popup},_movePopup:function(e){this._popup.setLatLng(e.latlng)}}),i.LayerGroup=i.Class.extend({initialize:function(e){this._layers={};var t,n;if(e)for(t=0,n=e.length;t';var n=e.firstChild;return n.style.behavior="url(#default#VML)",n&&typeof n.adj=="object"}catch(r){return!1}}(),i.Path=i.Browser.svg||!i.Browser.vml?i.Path:i.Path.extend({statics:{VML:!0,CLIP_PADDING:.02},_createElement:function(){try{return t.namespaces.add("lvml","urn:schemas-microsoft-com:vml"),function(e){return t.createElement("')}}catch(e){return function(e){return t.createElement("<"+e+' xmlns="urn:schemas-microsoft.com:vml" class="lvml">')}}}(),_initPath:function(){var e=this._container=this._createElement("shape");i.DomUtil.addClass(e,"leaflet-vml-shape"+(this.options.className?" "+this.options.className:"")),this.options.clickable&&i.DomUtil.addClass(e,"leaflet-clickable"),e.coordsize="1 1",this._path=this._createElement("path"),e.appendChild(this._path),this._map._pathRoot.appendChild(e)},_initStyle:function(){this._updateStyle()},_updateStyle:function(){var e=this._stroke,t=this._fill,n=this.options,r=this._container;r.stroked=n.stroke,r.filled=n.fill,n.stroke?(e||(e=this._stroke=this._createElement("stroke"),e.endcap="round",r.appendChild(e)),e.weight=n.weight+"px",e.color=n.color,e.opacity=n.opacity,n.dashArray?e.dashStyle=i.Util.isArray(n.dashArray)?n.dashArray.join(" "):n.dashArray.replace(/( *, *)/g," "):e.dashStyle="",n.lineCap&&(e.endcap=n.lineCap.replace("butt","flat")),n.lineJoin&&(e.joinstyle=n.lineJoin)):e&&(r.removeChild(e),this._stroke=null),n.fill?(t||(t=this._fill=this._createElement("fill"),r.appendChild(t)),t.color=n.fillColor||n.color,t.opacity=n.fillOpacity):t&&(r.removeChild(t),this._fill=null)},_updatePath:function(){var e=this._container.style;e.display="none",this._path.v=this.getPathString()+" ",e.display=""}}),i.Map.include(i.Browser.svg||!i.Browser.vml?{}:{_initPathRoot:function(){if(this._pathRoot)return;var e=this._pathRoot=t.createElement("div");e.className="leaflet-vml-container",this._panes.overlayPane.appendChild(e),this.on("moveend",this._updatePathViewport),this._updatePathViewport()}}),i.Browser.canvas=function(){return!!t.createElement("canvas").getContext}(),i.Path=i.Path.SVG&&!e.L_PREFER_CANVAS||!i.Browser.canvas?i.Path:i.Path.extend({statics:{CANVAS:!0,SVG:!1},redraw:function(){return this._map&&(this.projectLatlngs(),this._requestUpdate()),this},setStyle:function(e){return i.setOptions(this,e),this._map&&(this._updateStyle(),this._requestUpdate()),this},onRemove:function(e){e.off("viewreset",this.projectLatlngs,this).off("moveend",this._updatePath,this),this.options.clickable&&(this._map.off("click",this._onClick,this),this._map.off("mousemove",this._onMouseMove,this)),this._requestUpdate(),this._map=null},_requestUpdate:function(){this._map&&!i.Path._updateRequest&&(i.Path._updateRequest= +i.Util.requestAnimFrame(this._fireMapMoveEnd,this._map))},_fireMapMoveEnd:function(){i.Path._updateRequest=null,this.fire("moveend")},_initElements:function(){this._map._initPathRoot(),this._ctx=this._map._canvasCtx},_updateStyle:function(){var e=this.options;e.stroke&&(this._ctx.lineWidth=e.weight,this._ctx.strokeStyle=e.color),e.fill&&(this._ctx.fillStyle=e.fillColor||e.color)},_drawPath:function(){var e,t,n,r,s,o;this._ctx.beginPath();for(e=0,n=this._parts.length;es&&(o=u,s=a);s>n&&(t[o]=1,this._simplifyDPStep(e,t,n,r,o),this._simplifyDPStep(e,t,n,o,i))},_reducePoints:function(e,t){var n=[e[0]];for(var r=1,i=0,s=e.length;rt&&(n.push(e[r]),i=r);return it.max.x&&(n|=2),e.yt.max.y&&(n|=8),n},_sqDist:function(e,t){var n=t.x-e.x,r=t.y-e.y;return n*n+r*r},_sqClosestPointOnSegment:function(e,t,n,r){var s=t.x,o=t.y,u=n.x-s,a=n.y-o,f=u*u+a*a,l;return f>0&&(l=((e.x-s)*u+(e.y-o)*a)/f,l>1?(s=n.x,o=n.y):l>0&&(s+=u*l,o+=a*l)),u=e.x-s,a=e.y-o,r?u*u+a*a:new i.Point(s,o)}},i.Polyline=i.Path.extend({initialize:function(e,t){i.Path.prototype.initialize.call(this,t),this._latlngs=this._convertLatLngs(e)},options:{smoothFactor:1,noClip:!1},projectLatlngs:function(){this._originalPoints=[];for(var e=0,t=this._latlngs.length;e=2&&e[0].equals(e[e.length-1])&&e.pop()},projectLatlngs:function(){i.Polyline.prototype.projectLatlngs.call(this),this._holePoints=[];if(!this._holes)return;var e,t,n,r;for(e=0,n=this._holes.length;ee.max.x||n.y-t>e.max.y||n.x+te.y!=s.y>e.y&&e.x<(s.x-r.x)*(e.y-r.y)/(s.y-r.y)+r.x&&(t=!t)}return t}}:{}),i.Circle.include(i.Path.CANVAS?{_drawPath:function(){var e=this._point;this._ctx.beginPath(),this._ctx.arc(e.x,e.y,this._radius,0,Math.PI*2,!1)},_containsPoint:function(e){var t=this._point,n=this.options.stroke?this.options.weight/2:0;return e.distanceTo(t)<=this._radius+n}}:{}),i.CircleMarker.include(i.Path.CANVAS?{_updateStyle:function(){i.Path.prototype._updateStyle.call(this)}}:{}),i.GeoJSON=i.FeatureGroup.extend({initialize:function(e,t){i.setOptions(this,t),this._layers={},e&&this.addData(e)},addData:function(e){var t=i.Util.isArray(e)?e:e.features,n,r,s;if(t){for(n=0,r=t.length;n=0;n--)i.DomEvent.on(e,i.Draggable.START[n],t);return i.DomEvent.on(e,"click",i.DomEvent._fakeStop).on(e,"dblclick",t)},preventDefault:function(e){return e.preventDefault?e.preventDefault():e.returnValue=!1,this},stop:function(e){return i.DomEvent.preventDefault(e).stopPropagation(e)},getMousePosition:function(e,t){if(!t)return new i.Point(e.clientX,e.clientY);var n=t.getBoundingClientRect();return new i.Point(e.clientX-n.left-t.clientLeft,e.clientY-n.top-t.clientTop)},getWheelDelta:function(e){var t=0;return e.wheelDelta&&(t=e.wheelDelta/120),e.detail&&(t=-e.detail/3),t},_skipEvents:{},_fakeStop:function(e){i.DomEvent._skipEvents[e.type]=!0},_skipped:function(e){var t=this._skipEvents[e.type];return this._skipEvents[e.type]=!1,t},_checkMouse:function(e,t){var n=t.relatedTarget;if(!n)return!0;try{while(n&&n!==e)n=n.parentNode}catch(r){return!1}return n!==e},_getEvent:function(){var t=e.event;if(!t){var n=arguments.callee.caller;while(n){t=n.arguments[0];if(t&&e.Event===t.constructor)break;n=n.caller}}return t},_filterClick:function(e,t){var n=e.timeStamp||e.originalEvent.timeStamp,r=i.DomEvent._lastClick&&n-i.DomEvent._lastClick;if(r&&r>100&&r<1e3||e.target._simulatedClick&&!e._simulated){i.DomEvent.stop(e);return}return i.DomEvent._lastClick=n,t(e)}},i.DomEvent.on=i.DomEvent.addListener,i.DomEvent.off=i.DomEvent.removeListener,i.Draggable=i.Class.extend({includes:i.Mixin.Events,statics:{START:i.Browser.touch?["touchstart","mousedown"]:["mousedown"],END:{mousedown:"mouseup",touchstart:"touchend",pointerdown:"touchend",MSPointerDown:"touchend"},MOVE:{mousedown:"mousemove",touchstart:"touchmove",pointerdown:"touchmove",MSPointerDown:"touchmove"}},initialize:function(e,t){this._element=e,this._dragStartTarget=t||e},enable:function(){if(this._enabled)return;for(var e=i.Draggable.START.length-1;e>=0;e--)i.DomEvent.on(this._dragStartTarget,i.Draggable.START[e],this._onDown,this);this._enabled=!0},disable:function(){if(!this._enabled)return;for(var e=i.Draggable.START.length-1;e>=0;e--)i.DomEvent.off(this._dragStartTarget,i.Draggable.START[e],this._onDown,this);this._enabled=!1,this._moved=!1},_onDown:function(e){this._moved=!1;if(e.shiftKey||e.which!==1&&e.button!==1&&!e.touches)return;i.DomEvent.stopPropagation(e);if(i.Draggable._disabled)return;i.DomUtil.disableImageDrag(),i.DomUtil.disableTextSelection();if(this._moving)return;var n=e.touches?e.touches[0]:e;this._startPoint=new i.Point(n.clientX,n.clientY),this._startPos=this._newPos=i.DomUtil.getPosition(this._element),i.DomEvent.on(t,i.Draggable.MOVE[e.type],this._onMove,this).on(t,i.Draggable.END[e.type],this._onUp,this)},_onMove:function(e){if(e.touches&&e.touches.length>1){this._moved=!0;return}var n=e.touches&&e.touches.length===1?e.touches[0]:e,r=new i.Point(n.clientX,n.clientY),s=r.subtract(this._startPoint);if(!s.x&&!s.y)return;i.DomEvent.preventDefault(e),this._moved||(this.fire("dragstart"),this._moved=!0,this._startPos=i.DomUtil.getPosition(this._element).subtract(s),i.DomUtil.addClass(t.body,"leaflet-dragging"),i.DomUtil.addClass(e.target||e.srcElement,"leaflet-drag-target")),this._newPos=this._startPos.add(s),this._moving=!0,i.Util.cancelAnimFrame(this._animRequest),this._animRequest=i.Util.requestAnimFrame(this._updatePosition,this,!0,this._dragStartTarget)},_updatePosition:function(){this.fire("predrag"),i.DomUtil.setPosition(this._element,this._newPos),this.fire("drag")},_onUp:function(e){i.DomUtil.removeClass(t.body,"leaflet-dragging"),i.DomUtil.removeClass(e.target||e.srcElement,"leaflet-drag-target");for(var n in i.Draggable.MOVE)i.DomEvent.off(t,i.Draggable.MOVE[n],this._onMove).off(t,i.Draggable.END[n],this._onUp);i.DomUtil.enableImageDrag(),i.DomUtil.enableTextSelection(),this._moved&&this._moving&&(i.Util.cancelAnimFrame(this._animRequest),this.fire("dragend",{distance:this._newPos.distanceTo(this._startPos)})),this._moving=!1}}),i.Handler=i.Class.extend({initialize:function(e){this._map=e},enable:function(){if(this._enabled)return;this._enabled=!0,this.addHooks()},disable:function(){if(!this._enabled)return;this._enabled=!1,this.removeHooks()},enabled:function(){return!!this._enabled}}),i.Map.mergeOptions({dragging:!0,inertia:!i.Browser.android23,inertiaDeceleration:3400,inertiaMaxSpeed:Infinity,inertiaThreshold:i.Browser.touch?32:18,easeLinearity:.25,worldCopyJump:!1}),i.Map.Drag=i.Handler.extend({addHooks:function(){if(!this._draggable){var e=this._map;this._draggable=new i.Draggable(e._mapPane,e._container),this._draggable.on({dragstart:this._onDragStart,drag:this._onDrag,dragend:this._onDragEnd},this),e.options.worldCopyJump&&(this._draggable.on("predrag",this._onPreDrag,this),e.on("viewreset",this._onViewReset,this),e.whenReady(this._onViewReset,this))}this._draggable.enable()},removeHooks:function(){this._draggable.disable()},moved:function(){return this._draggable&&this._draggable._moved},_onDragStart:function(){var e=this._map;e._panAnim&&e._panAnim.stop(),e.fire("movestart").fire("dragstart"),e.options.inertia&&(this._positions=[],this._times=[])},_onDrag:function(){if(this._map.options.inertia){var e=this._lastTime=+(new Date),t=this._lastPos=this._draggable._newPos;this._positions.push(t),this._times.push(e),e-this._times[0]>200&&(this._positions.shift(),this._times.shift())}this._map.fire("move").fire("drag")},_onViewReset:function(){var e=this._map.getSize()._divideBy(2),t=this._map.latLngToLayerPoint([0,0]);this._initialWorldOffset=t.subtract(e).x,this._worldWidth=this._map.project([0,180]).x},_onPreDrag:function(){var e=this._worldWidth,t=Math.round(e/2),n=this._initialWorldOffset,r=this._draggable._newPos.x,i=(r-t+n)%e+t-n,s=(r+t+n)%e-t-n,o=Math.abs(i+n)n.inertiaThreshold||!this._positions[0];t.fire("dragend",e);if(s)t.fire("moveend");else{var o=this._lastPos.subtract(this._positions[0]),u=(this._lastTime+r-this._times[0])/1e3,a=n.easeLinearity,f=o.multiplyBy(a/u),l=f.distanceTo([0,0]),c=Math.min(n.inertiaMaxSpeed,l),h=f.multiplyBy(c/l),p=c/(n.inertiaDeceleration*a),d=h.multiplyBy(-p/2).round();!d.x||!d.y?t.fire("moveend"):(d=t._limitOffset(d,t.options.maxBounds),i.Util.requestAnimFrame(function(){t.panBy(d,{duration:p,easeLinearity:a,noMoveStart:!0})}))}}}),i.Map.addInitHook("addHandler","dragging",i.Map.Drag),i.Map.mergeOptions({doubleClickZoom:!0}),i.Map.DoubleClickZoom=i.Handler.extend({addHooks:function(){this._map.on("dblclick",this._onDoubleClick,this)},removeHooks:function(){this._map.off("dblclick",this._onDoubleClick,this)},_onDoubleClick:function(e){var t=this._map,n=t.getZoom()+(e.originalEvent.shiftKey?-1:1);t.options.doubleClickZoom==="center"?t.setZoom(n):t.setZoomAround(e.containerPoint,n)}}),i.Map.addInitHook("addHandler","doubleClickZoom",i.Map.DoubleClickZoom),i.Map.mergeOptions({scrollWheelZoom:!0}),i.Map.ScrollWheelZoom=i.Handler.extend({addHooks:function(){i.DomEvent.on(this._map._container,"mousewheel",this._onWheelScroll,this),i.DomEvent.on(this._map._container,"MozMousePixelScroll",i.DomEvent.preventDefault),this._delta=0},removeHooks:function(){i.DomEvent.off(this._map._container,"mousewheel",this._onWheelScroll),i.DomEvent.off(this._map._container,"MozMousePixelScroll",i.DomEvent.preventDefault)},_onWheelScroll:function(e){var t=i.DomEvent.getWheelDelta(e);this._delta+=t,this._lastMousePos=this._map.mouseEventToContainerPoint(e),this._startTime||(this._startTime=+(new Date));var n=Math.max(40-(+(new Date)-this._startTime),0);clearTimeout(this._timer),this._timer=setTimeout(i.bind(this._performZoom,this),n),i.DomEvent.preventDefault(e),i.DomEvent.stopPropagation(e)},_performZoom:function(){var e=this._map,t=this._delta,n=e.getZoom();t=t>0?Math.ceil(t):Math.floor(t),t=Math.max(Math.min(t,4),-4),t=e._limitZoom(n+t)-n,this._delta=0,this._startTime=null;if(!t)return;e.options.scrollWheelZoom==="center"?e.setZoom(n+t):e.setZoomAround(this._lastMousePos,n+t)}}),i.Map.addInitHook("addHandler","scrollWheelZoom",i.Map.ScrollWheelZoom),i.extend(i.DomEvent,{_touchstart:i.Browser.msPointer?"MSPointerDown":i.Browser.pointer?"pointerdown":"touchstart",_touchend:i.Browser.msPointer?"MSPointerUp":i.Browser.pointer?"pointerup":"touchend",addDoubleTapListener:function(e,n,r){function p(e){var t;i.Browser.pointer?(h.push(e.pointerId),t=h.length):t=e.touches.length;if(t>1)return;var n=Date.now(),r=n-(s||n);a=e.touches?e.touches[0]:e,o=r>0&&r<=u,s=n}function d(e){if(i.Browser.pointer){var t=h.indexOf(e.pointerId);if(t===-1)return;h.splice(t,1)}if(o){if(i.Browser.pointer){var r={},u;for(var f in a)u=a[f],typeof u=="function"?r[f]=u.bind(a):r[f]=u;a=r}a.type="dblclick",n(a),s=null}}var s,o=!1,u=250,a,f="_leaflet_",l=this._touchstart,c=this._touchend,h=[];e[f+l+r]=p,e[f+c+r]=d;var v=i.Browser.pointer?t.documentElement:e;return e.addEventListener(l,p,!1),v.addEventListener(c,d,!1),i.Browser.pointer&&v.addEventListener(i.DomEvent.POINTER_CANCEL,d,!1),this},removeDoubleTapListener:function(e,n){var r="_leaflet_";return e.removeEventListener(this._touchstart,e[r+this._touchstart+n],!1),(i.Browser.pointer?t.documentElement:e).removeEventListener(this._touchend,e[r+this._touchend+n],!1),i.Browser.pointer&&t.documentElement.removeEventListener(i.DomEvent.POINTER_CANCEL,e[r+this._touchend+n],!1),this}}),i.extend(i.DomEvent,{POINTER_DOWN:i.Browser.msPointer?"MSPointerDown":"pointerdown",POINTER_MOVE:i.Browser.msPointer?"MSPointerMove":"pointermove",POINTER_UP:i.Browser.msPointer?"MSPointerUp":"pointerup",POINTER_CANCEL:i.Browser.msPointer?"MSPointerCancel":"pointercancel",_pointers:[],_pointerDocumentListener:!1,addPointerListener:function(e,t,n,r){switch(t){case"touchstart":return this.addPointerListenerStart(e,t,n,r);case"touchend":return this.addPointerListenerEnd(e,t,n,r);case"touchmove":return this.addPointerListenerMove(e,t,n,r);default:throw"Unknown touch event type"}},addPointerListenerStart:function(e,n,r,s){var o="_leaflet_",u=this._pointers,a=function(e){i.DomEvent.preventDefault(e);var t=!1;for(var n=0;n1)return;this._moved||(i.DomUtil.addClass(t._mapPane,"leaflet-touching"),t.fire("movestart").fire("zoomstart"),this._moved=!0),i.Util.cancelAnimFrame(this._animRequest),this._animRequest=i.Util.requestAnimFrame(this._updateOnMove,this,!0,this._map._container),i.DomEvent.preventDefault(e)},_updateOnMove:function(){var e=this._map,t=this._getScaleOrigin(),n=e.layerPointToLatLng(t),r=e.getScaleZoom(this._scale);e._animateZoom(n,r,this._startCenter,this._scale,this._delta)},_onTouchEnd:function(){if(!this._moved||!this._zooming){this._zooming=!1;return}var e=this._map;this._zooming=!1,i.DomUtil.removeClass(e._mapPane,"leaflet-touching"),i.Util.cancelAnimFrame(this._animRequest),i.DomEvent.off(t,"touchmove",this._onTouchMove).off(t,"touchend",this._onTouchEnd);var n=this._getScaleOrigin(),r=e.layerPointToLatLng(n),s=e.getZoom(),o=e.getScaleZoom(this._scale)-s,u=o>0?Math.ceil(o):Math.floor(o),a=e._limitZoom(s+u),f=e.getZoomScale(a)/this._scale;e._animateZoom(r,a,n,f)},_getScaleOrigin:function(){var e=this._centerOffset.subtract(this._delta).divideBy(this._scale);return this._startCenter.add(e)}}),i.Map.addInitHook("addHandler","touchZoom",i.Map.TouchZoom),i.Map.mergeOptions({tap:!0,tapTolerance:15}),i.Map.Tap=i.Handler.extend({addHooks:function(){i.DomEvent.on(this._map._container,"touchstart",this._onDown,this)},removeHooks:function(){i.DomEvent.off(this._map._container,"touchstart",this._onDown,this)},_onDown:function(e){if(!e.touches)return;i.DomEvent.preventDefault(e),this._fireClick=!0;if(e.touches.length>1){this._fireClick=!1,clearTimeout(this._holdTimeout);return}var n=e.touches[0],r=n.target;this._startPos=this._newPos=new i.Point(n.clientX,n.clientY),r.tagName&&r.tagName.toLowerCase()==="a"&&i.DomUtil.addClass(r,"leaflet-active"),this._holdTimeout=setTimeout(i.bind(function(){this._isTapValid()&&(this._fireClick=!1,this._onUp(),this._simulateEvent("contextmenu",n))},this),1e3),i.DomEvent.on(t,"touchmove",this._onMove,this).on(t,"touchend",this._onUp,this)},_onUp:function(e){clearTimeout(this._holdTimeout),i.DomEvent.off(t,"touchmove",this._onMove,this).off(t,"touchend",this._onUp,this);if(this._fireClick&& +e&&e.changedTouches){var n=e.changedTouches[0],r=n.target;r&&r.tagName&&r.tagName.toLowerCase()==="a"&&i.DomUtil.removeClass(r,"leaflet-active"),this._isTapValid()&&this._simulateEvent("click",n)}},_isTapValid:function(){return this._newPos.distanceTo(this._startPos)<=this._map.options.tapTolerance},_onMove:function(e){var t=e.touches[0];this._newPos=new i.Point(t.clientX,t.clientY)},_simulateEvent:function(n,r){var i=t.createEvent("MouseEvents");i._simulated=!0,r.target._simulatedClick=!0,i.initMouseEvent(n,!0,!0,e,1,r.screenX,r.screenY,r.clientX,r.clientY,!1,!1,!1,!1,0,null),r.target.dispatchEvent(i)}}),i.Browser.touch&&!i.Browser.pointer&&i.Map.addInitHook("addHandler","tap",i.Map.Tap),i.Map.mergeOptions({boxZoom:!0}),i.Map.BoxZoom=i.Handler.extend({initialize:function(e){this._map=e,this._container=e._container,this._pane=e._panes.overlayPane,this._moved=!1},addHooks:function(){i.DomEvent.on(this._container,"mousedown",this._onMouseDown,this)},removeHooks:function(){i.DomEvent.off(this._container,"mousedown",this._onMouseDown),this._moved=!1},moved:function(){return this._moved},_onMouseDown:function(e){this._moved=!1;if(!e.shiftKey||e.which!==1&&e.button!==1)return!1;i.DomUtil.disableTextSelection(),i.DomUtil.disableImageDrag(),this._startLayerPoint=this._map.mouseEventToLayerPoint(e),i.DomEvent.on(t,"mousemove",this._onMouseMove,this).on(t,"mouseup",this._onMouseUp,this).on(t,"keydown",this._onKeyDown,this)},_onMouseMove:function(e){this._moved||(this._box=i.DomUtil.create("div","leaflet-zoom-box",this._pane),i.DomUtil.setPosition(this._box,this._startLayerPoint),this._container.style.cursor="crosshair",this._map.fire("boxzoomstart"));var t=this._startLayerPoint,n=this._box,r=this._map.mouseEventToLayerPoint(e),s=r.subtract(t),o=new i.Point(Math.min(r.x,t.x),Math.min(r.y,t.y));i.DomUtil.setPosition(n,o),this._moved=!0,n.style.width=Math.max(0,Math.abs(s.x)-4)+"px",n.style.height=Math.max(0,Math.abs(s.y)-4)+"px"},_finish:function(){this._moved&&(this._pane.removeChild(this._box),this._container.style.cursor=""),i.DomUtil.enableTextSelection(),i.DomUtil.enableImageDrag(),i.DomEvent.off(t,"mousemove",this._onMouseMove).off(t,"mouseup",this._onMouseUp).off(t,"keydown",this._onKeyDown)},_onMouseUp:function(e){this._finish();var t=this._map,n=t.mouseEventToLayerPoint(e);if(this._startLayerPoint.equals(n))return;var r=new i.LatLngBounds(t.layerPointToLatLng(this._startLayerPoint),t.layerPointToLatLng(n));t.fitBounds(r),t.fire("boxzoomend",{boxZoomBounds:r})},_onKeyDown:function(e){e.keyCode===27&&this._finish()}}),i.Map.addInitHook("addHandler","boxZoom",i.Map.BoxZoom),i.Map.mergeOptions({keyboard:!0,keyboardPanOffset:80,keyboardZoomOffset:1}),i.Map.Keyboard=i.Handler.extend({keyCodes:{left:[37],right:[39],down:[40],up:[38],zoomIn:[187,107,61,171],zoomOut:[189,109,173]},initialize:function(e){this._map=e,this._setPanOffset(e.options.keyboardPanOffset),this._setZoomOffset(e.options.keyboardZoomOffset)},addHooks:function(){var e=this._map._container;e.tabIndex===-1&&(e.tabIndex="0"),i.DomEvent.on(e,"focus",this._onFocus,this).on(e,"blur",this._onBlur,this).on(e,"mousedown",this._onMouseDown,this),this._map.on("focus",this._addHooks,this).on("blur",this._removeHooks,this)},removeHooks:function(){this._removeHooks();var e=this._map._container;i.DomEvent.off(e,"focus",this._onFocus,this).off(e,"blur",this._onBlur,this).off(e,"mousedown",this._onMouseDown,this),this._map.off("focus",this._addHooks,this).off("blur",this._removeHooks,this)},_onMouseDown:function(){if(this._focused)return;var n=t.body,r=t.documentElement,i=n.scrollTop||r.scrollTop,s=n.scrollLeft||r.scrollLeft;this._map._container.focus(),e.scrollTo(s,i)},_onFocus:function(){this._focused=!0,this._map.fire("focus")},_onBlur:function(){this._focused=!1,this._map.fire("blur")},_setPanOffset:function(e){var t=this._panKeys={},n=this.keyCodes,r,i;for(r=0,i=n.left.length;rLeaflet'},initialize:function(e){i.setOptions(this,e),this._attributions={}},onAdd:function(e){this._container=i.DomUtil.create("div","leaflet-control-attribution"),i.DomEvent.disableClickPropagation(this._container);for(var t in e._layers)e._layers[t].getAttribution&&this.addAttribution(e._layers[t].getAttribution());return e.on("layeradd",this._onLayerAdd,this).on("layerremove",this._onLayerRemove,this),this._update(),this._container},onRemove:function(e){e.off("layeradd",this._onLayerAdd).off("layerremove",this._onLayerRemove)},setPrefix:function(e){return this.options.prefix=e,this._update(),this},addAttribution:function(e){if(!e)return;return this._attributions[e]||(this._attributions[e]=0),this._attributions[e]++,this._update(),this},removeAttribution:function(e){if(!e)return;return this._attributions[e]&&(this._attributions[e]--,this._update()),this},_update:function(){if(!this._map)return;var e=[];for(var t in this._attributions)this._attributions[t]&&e.push(t);var n=[];this.options.prefix&&n.push(this.options.prefix),e.length&&n.push(e.join(", ")),this._container.innerHTML=n.join(" | ")},_onLayerAdd:function(e){e.layer.getAttribution&&this.addAttribution(e.layer.getAttribution())},_onLayerRemove:function(e){e.layer.getAttribution&&this.removeAttribution(e.layer.getAttribution())}}),i.Map.mergeOptions({attributionControl:!0}),i.Map.addInitHook(function(){this.options.attributionControl&&(this.attributionControl=(new i.Control.Attribution).addTo(this))}),i.control.attribution=function(e){return new i.Control.Attribution(e)},i.Control.Scale=i.Control.extend({options:{position:"bottomleft",maxWidth:100,metric:!0,imperial:!0,updateWhenIdle:!1},onAdd:function(e){this._map=e;var t="leaflet-control-scale",n=i.DomUtil.create("div",t),r=this.options;return this._addScales(r,t,n),e.on(r.updateWhenIdle?"moveend":"move",this._update,this),e.whenReady(this._update,this),n},onRemove:function(e){e.off(this.options.updateWhenIdle?"moveend":"move",this._update,this)},_addScales:function(e,t,n){e.metric&&(this._mScale=i.DomUtil.create("div",t+"-line",n)),e.imperial&&(this._iScale=i.DomUtil.create("div",t+"-line",n))},_update:function(){var e=this._map.getBounds(),t=e.getCenter().lat,n=6378137*Math.PI*Math.cos(t*Math.PI/180),r=n*(e.getNorthEast().lng-e.getSouthWest().lng)/180,i=this._map.getSize(),s=this.options,o=0;i.x>0&&(o=r*(s.maxWidth/i.x)),this._updateScales(s,o)},_updateScales:function(e,t){e.metric&&t&&this._updateMetric(t),e.imperial&&t&&this._updateImperial(t)},_updateMetric:function(e){var t=this._getRoundNum(e);this._mScale.style.width=this._getScaleWidth(t/e)+"px",this._mScale.innerHTML=t<1e3?t+" m":t/1e3+" km"},_updateImperial:function(e){var t=e*3.2808399,n=this._iScale,r,i,s;t>5280?(r=t/5280,i=this._getRoundNum(r),n.style.width=this._getScaleWidth(i/r)+"px",n.innerHTML=i+" mi"):(s=this._getRoundNum(t),n.style.width=this._getScaleWidth(s/t)+"px",n.innerHTML=s+" ft")},_getScaleWidth:function(e){return Math.round(this.options.maxWidth*e)-10},_getRoundNum:function(e){var t=Math.pow(10,(Math.floor(e)+"").length-1),n=e/t;return n=n>=10?10:n>=5?5:n>=3?3:n>=2?2:1,t*n}}),i.control.scale=function(e){return new i.Control.Scale(e)},i.Control.Layers=i.Control.extend({options:{collapsed:!0,position:"topright",autoZIndex:!0},initialize:function(e,t,n){i.setOptions(this,n),this._layers={},this._lastZIndex=0,this._handlingClick=!1;for(var r in e)this._addLayer(e[r],r);for(r in t)this._addLayer(t[r],r,!0)},onAdd:function(e){return this._initLayout(),this._update(),e.on("layeradd",this._onLayerChange,this).on("layerremove",this._onLayerChange,this),this._container},onRemove:function(e){e.off("layeradd",this._onLayerChange).off("layerremove",this._onLayerChange)},addBaseLayer:function(e,t){return this._addLayer(e,t),this._update(),this},addOverlay:function(e,t){return this._addLayer(e,t,!0),this._update(),this},removeLayer:function(e){var t=i.stamp(e);return delete this._layers[t],this._update(),this},_initLayout:function(){var e="leaflet-control-layers",t=this._container=i.DomUtil.create("div",e);t.setAttribute("aria-haspopup",!0),i.Browser.touch?i.DomEvent.on(t,"click",i.DomEvent.stopPropagation):i.DomEvent.disableClickPropagation(t).disableScrollPropagation(t);var n=this._form=i.DomUtil.create("form",e+"-list");if(this.options.collapsed){i.Browser.android||i.DomEvent.on(t,"mouseover",this._expand,this).on(t,"mouseout",this._collapse,this);var r=this._layersLink=i.DomUtil.create("a",e+"-toggle",t);r.href="#",r.title="Layers",i.Browser.touch?i.DomEvent.on(r,"click",i.DomEvent.stop).on(r,"click",this._expand,this):i.DomEvent.on(r,"focus",this._expand,this),i.DomEvent.on(n,"click",function(){setTimeout(i.bind(this._onInputClick,this),0)},this),this._map.on("click",this._collapse,this)}else this._expand();this._baseLayersList=i.DomUtil.create("div",e+"-base",n),this._separator=i.DomUtil.create("div",e+"-separator",n),this._overlaysList=i.DomUtil.create("div",e+"-overlays",n),t.appendChild(n)},_addLayer:function(e,t,n){var r=i.stamp(e);this._layers[r]={layer:e,name:t,overlay:n},this.options.autoZIndex&&e.setZIndex&&(this._lastZIndex++,e.setZIndex(this._lastZIndex))},_update:function(){if(!this._container)return;this._baseLayersList.innerHTML="",this._overlaysList.innerHTML="";var e=!1,t=!1,n,r;for(n in this._layers)r=this._layers[n],this._addItem(r),t=t||r.overlay,e=e||!r.overlay;this._separator.style.display=t&&e?"":"none"},_onLayerChange:function(e){var t=this._layers[i.stamp(e.layer)];if(!t)return;this._handlingClick||this._update();var n=t.overlay?e.type==="layeradd"?"overlayadd":"overlayremove":e.type==="layeradd"?"baselayerchange":null;n&&this._map.fire(n,t)},_createRadioElement:function(e,n){var r='=0&&this._onZoomTransitionEnd()},_nothingToAnimate:function(){return!this._container.getElementsByClassName("leaflet-zoom-animated").length},_tryAnimatedZoom:function(e,t,n){if(this._animatingZoom)return!0;n=n||{};if(!this._zoomAnimated||n.animate===!1||this._nothingToAnimate()||Math.abs(t-this._zoom)>this.options.zoomAnimationThreshold)return!1;var r=this.getZoomScale(t),i=this._getCenterOffset(e)._divideBy(1-1/r),s=this._getCenterLayerPoint()._add(i);return n.animate!==!0&&!this.getSize().contains(i)?!1:(this.fire("movestart").fire("zoomstart"),this._animateZoom(e,t,s,r,null,!0),!0)},_animateZoom:function(e,t,n,r,s,o){this._animatingZoom=!0,i.DomUtil.addClass(this._mapPane,"leaflet-zoom-anim"),this._animateToCenter=e,this._animateToZoom=t,i.Draggable&&(i.Draggable._disabled=!0),this.fire("zoomanim",{center:e,zoom:t,origin:n,scale:r,delta:s,backwards:o})},_onZoomTransitionEnd:function(){this._animatingZoom=!1,i.DomUtil.removeClass(this._mapPane,"leaflet-zoom-anim"),this._resetView(this._animateToCenter,this._animateToZoom,!0,!0),i.Draggable&&(i.Draggable._disabled=!1)}}:{}),i.TileLayer.include({_animateZoom:function(e){this._animating||(this._animating=!0,this._prepareBgBuffer());var t=this._bgBuffer,n=i.DomUtil.TRANSFORM,r=e.delta?i.DomUtil.getTranslateString(e.delta):t.style[n],s=i.DomUtil.getScaleString(e.scale,e.origin);t.style[n]=e.backwards?s+" "+r:r+" "+s},_endZoomAnim:function(){var e=this._tileContainer,t=this._bgBuffer;e.style.visibility="",e.parentNode.appendChild(e),i.Util.falseFn(t.offsetWidth),this._animating=!1},_clearBgBuffer:function(){var e=this._map;e&&!e._animatingZoom&&!e.touchZoom._zooming&&(this._bgBuffer.innerHTML="",this._bgBuffer.style[i.DomUtil.TRANSFORM]="")},_prepareBgBuffer:function(){var e=this._tileContainer,t=this._bgBuffer,n=this._getLoadedTilesPercentage(t),r=this._getLoadedTilesPercentage(e);if(t&&n>.5&&r<.5){e.style.visibility="hidden",this._stopLoadingImages(e);return}t.style.visibility="hidden",t.style[i.DomUtil.TRANSFORM]="",this._tileContainer=t,t=this._bgBuffer=e,this._stopLoadingImages(t),clearTimeout(this._clearBgBufferTimer)},_getLoadedTilesPercentage:function(e){var t=e.getElementsByTagName("img"),n,r,i=0;for(n=0,r=t.length;n0){t=t.split(" ");for(f=t.length;f--;)P(e,t[f],n);return e}u=c&&t.replace(o,""),u&&S[u]&&(u=S[u].type);if(!t||c){if(a=c&&t.replace(s,""))a=a.split(".");l(e,u,n,a)}else if(typeof t=="function")l(e,null,t);else for(r in t)t.hasOwnProperty(r)&&P(e,r,t[r]);return e},H=function(e,t,n,r,i){var s,o,u,a,f=n,l=n&&typeof n=="string";if(t&&!n&&typeof t=="object")for(s in t)t.hasOwnProperty(s)&&H.apply(this,[e,s,t[s]]);else{a=arguments.length>3?v.call(arguments,3):[],o=(l?n:t).split(" "),l&&(n=D(t,f=r,i))&&(a=v.call(a,1)),this===w&&(n=O(P,e,t,n,f));for(u=o.length;u--;)_(e,o[u],n,f,a)}return e},B=function(){return H.apply(w,arguments)},j=p?function(e,t,r){var i=c.createEvent(e?"HTMLEvents":"UIEvents");i[e?"initEvent":"initUIEvent"](t,!0,!0,n,1),r.dispatchEvent(i)}:function(e,t,n){n=T(n,e),e?n.fireEvent("on"+t,c.createEventObject()):n["_on"+t]++},F=function(e,t,n){var r,i,u,a,f,l=t.split(" ");for(r=l.length;r--;){t=l[r].replace(o,"");if(a=l[r].replace(s,""))a=a.split(".");if(!a&&!n&&e[d])j(E[t],t,e);else{f=C.get(e,t),n=[!1].concat(n);for(i=0,u=f.length;i"?">":"&",s)}}n.endDoc&&n.endDoc(s)}}function x(t){var n,r;return S({startDoc:function(e){n=[],r=!1},startTag:function(i,s,o){if(r)return;if(!e.ELEMENTS.hasOwnProperty(i))return;var u=e.ELEMENTS[i];if(u&e.eflags.FOLDABLE)return;if(u&e.eflags.UNSAFE){r=!(u&e.eflags.EMPTY);return}s=t(i,s);if(s){u&e.eflags.EMPTY||n.push(i),o.push("<",i);for(var a=0,f=s.length;a")}},endTag:function(t,i){if(r){r=!1;return}if(!e.ELEMENTS.hasOwnProperty(t))return;var s=e.ELEMENTS[t];if(!(s&(e.eflags.UNSAFE|e.eflags.EMPTY|e.eflags.FOLDABLE))){var o;if(s&e.eflags.OPTIONAL_ENDTAG)for(o=n.length;--o>=0;){var u=n[o];if(u===t)break;if(!(e.ELEMENTS[u]&e.eflags.OPTIONAL_ENDTAG))return}else for(o=n.length;--o>=0;)if(n[o]===t)break;if(o<0)return;for(var a=n.length;--a>o;){var u=n[a];e.ELEMENTS[u]&e.eflags.OPTIONAL_ENDTAG||i.push("")}n.length=o,i.push("")}},pcdata:function(e,t){r||t.push(e)},rcdata:function(e,t){r||t.push(e)},cdata:function(e,t){r||t.push(e)},endDoc:function(e){for(var t=n.length;--t>=0;)e.push("");n.length=0}})}function N(t,n,i){var s=[];return x(function(s,o){for(var u=0;u",amp:"&",nbsp:" ",quot:'"',apos:"'"},r=/^(?:https?|mailto|data)$/i,i=/^#(\d+)$/,s=/^#x([0-9A-Fa-f]+)$/,a=/\0/g,l=/&(#\d+|#x[0-9A-Fa-f]+|\w+);/g,h=/&/g,p=/&([^a-z#]|#(?:[^0-9x]|x(?:[^0-9a-f]|$)|$)|$)/gi,d=//g,m=/\"/g,g=/\=/g,w=new RegExp("^\\s*(?:(?:([a-z][a-z-]*)(\\s*=\\s*(\"[^\"]*\"|'[^']*'|(?=[a-z][a-z-]*\\s*=)|[^>\"'\\s]*))?)|(/?>)|[\\s\\S][^a-z\\s>]*)","i"),E=new RegExp("^(?:&(\\#[0-9]+|\\#[x][0-9a-f]+|\\w+);||]*>|<\\?[^>*]*>|<(/)?([a-z][a-z0-9]*)|([^<&>]+)|([<&>]))","i"),T=new RegExp("^(?:([^:/?#]+):)?");return{escapeAttrib:y,makeHtmlSanitizer:x,makeSaxParser:S,normalizeRCData:b,sanitize:N,unescapeEntities:c}}(html4),html_sanitize=html.sanitize;typeof window!="undefined"&&(window.html=html,window.html_sanitize=html_sanitize),html4.ATTRIBS["*::style"]=0,html4.ELEMENTS.style=0,html4.ATTRIBS["a::target"]=0,html4.ELEMENTS.video=0,html4.ATTRIBS["video::src"]=0,html4.ATTRIBS["video::poster"]=0,html4.ATTRIBS["video::controls"]=0,html4.ELEMENTS.audio=0,html4.ATTRIBS["audio::src"]=0,html4.ATTRIBS["video::autoplay"]=0,html4.ATTRIBS["video::controls"]=0;var Mustache=typeof module!="undefined"&&module.exports||{};(function(e){function a(e){return u.test(e)}function p(e){return String(e).replace(/[&<>"'\/]/g,function(e){return h[e]||e})}function d(e,t,n,r){r=r||"