diff --git a/note.html b/note.html index a62a6095d..7274dd9dd 100755 --- a/note.html +++ b/note.html @@ -22,6 +22,8 @@ + + @@ -665,12 +667,15 @@ + + + - + diff --git a/public/css-libs/emoji.css b/public/css-libs/emoji.css new file mode 100644 index 000000000..7d1a2b8e5 --- /dev/null +++ b/public/css-libs/emoji.css @@ -0,0 +1 @@ +.em,.em-svg{height:1.5em;width:1.5em;background-position:center;background-repeat:no-repeat;background-size:contain;display:inline-block;vertical-align:middle}.em---1{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f44d.png")}.em---1.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f44d.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f44d.svg"),none}.em--1{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f44e.png")}.em--1.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f44e.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f44e.svg"),none}.em-100{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f4af.png")}.em-100.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f4af.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f4af.svg"),none}.em-1234{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f522.png")}.em-1234.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f522.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f522.svg"),none}.em-8ball{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f3b1.png")}.em-8ball.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f3b1.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f3b1.svg"),none}.em-a{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f170.png")}.em-a.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f170.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f170.svg"),none}.em-ab{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f18e.png")}.em-ab.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f18e.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f18e.svg"),none}.em-abc{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f524.png")}.em-abc.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f524.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f524.svg"),none}.em-abcd{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f521.png")}.em-abcd.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f521.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f521.svg"),none}.em-accept{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f251.png")}.em-accept.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f251.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f251.svg"),none}.em-admission_tickets{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f39f.png")}.em-admission_tickets.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f39f.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f39f.svg"),none}.em-adult{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f9d1.png")}.em-adult.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f9d1.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f9d1.svg"),none}.em-aerial_tramway{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f6a1.png")}.em-aerial_tramway.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f6a1.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f6a1.svg"),none}.em-airplane{background-image:url("https://twemoji.maxcdn.com/2/72x72/2708.png")}.em-airplane.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/2708.png");background-image:url("https://twemoji.maxcdn.com/2/svg/2708.svg"),none}.em-airplane_arriving{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f6ec.png")}.em-airplane_arriving.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f6ec.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f6ec.svg"),none}.em-airplane_departure{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f6eb.png")}.em-airplane_departure.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f6eb.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f6eb.svg"),none}.em-alarm_clock{background-image:url("https://twemoji.maxcdn.com/2/72x72/23f0.png")}.em-alarm_clock.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/23f0.png");background-image:url("https://twemoji.maxcdn.com/2/svg/23f0.svg"),none}.em-alembic{background-image:url("https://twemoji.maxcdn.com/2/72x72/2697.png")}.em-alembic.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/2697.png");background-image:url("https://twemoji.maxcdn.com/2/svg/2697.svg"),none}.em-alien{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f47d.png")}.em-alien.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f47d.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f47d.svg"),none}.em-ambulance{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f691.png")}.em-ambulance.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f691.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f691.svg"),none}.em-amphora{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f3fa.png")}.em-amphora.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f3fa.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f3fa.svg"),none}.em-anchor{background-image:url("https://twemoji.maxcdn.com/2/72x72/2693.png")}.em-anchor.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/2693.png");background-image:url("https://twemoji.maxcdn.com/2/svg/2693.svg"),none}.em-angel{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f47c.png")}.em-angel.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f47c.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f47c.svg"),none}.em-anger{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f4a2.png")}.em-anger.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f4a2.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f4a2.svg"),none}.em-angry{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f620.png")}.em-angry.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f620.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f620.svg"),none}.em-anguished{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f627.png")}.em-anguished.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f627.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f627.svg"),none}.em-ant{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f41c.png")}.em-ant.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f41c.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f41c.svg"),none}.em-apple{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f34e.png")}.em-apple.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f34e.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f34e.svg"),none}.em-aquarius{background-image:url("https://twemoji.maxcdn.com/2/72x72/2652.png")}.em-aquarius.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/2652.png");background-image:url("https://twemoji.maxcdn.com/2/svg/2652.svg"),none}.em-aries{background-image:url("https://twemoji.maxcdn.com/2/72x72/2648.png")}.em-aries.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/2648.png");background-image:url("https://twemoji.maxcdn.com/2/svg/2648.svg"),none}.em-arrow_backward{background-image:url("https://twemoji.maxcdn.com/2/72x72/25c0.png")}.em-arrow_backward.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/25c0.png");background-image:url("https://twemoji.maxcdn.com/2/svg/25c0.svg"),none}.em-arrow_double_down{background-image:url("https://twemoji.maxcdn.com/2/72x72/23ec.png")}.em-arrow_double_down.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/23ec.png");background-image:url("https://twemoji.maxcdn.com/2/svg/23ec.svg"),none}.em-arrow_double_up{background-image:url("https://twemoji.maxcdn.com/2/72x72/23eb.png")}.em-arrow_double_up.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/23eb.png");background-image:url("https://twemoji.maxcdn.com/2/svg/23eb.svg"),none}.em-arrow_down{background-image:url("https://twemoji.maxcdn.com/2/72x72/2b07.png")}.em-arrow_down.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/2b07.png");background-image:url("https://twemoji.maxcdn.com/2/svg/2b07.svg"),none}.em-arrow_down_small{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f53d.png")}.em-arrow_down_small.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f53d.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f53d.svg"),none}.em-arrow_forward{background-image:url("https://twemoji.maxcdn.com/2/72x72/25b6.png")}.em-arrow_forward.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/25b6.png");background-image:url("https://twemoji.maxcdn.com/2/svg/25b6.svg"),none}.em-arrow_heading_down{background-image:url("https://twemoji.maxcdn.com/2/72x72/2935.png")}.em-arrow_heading_down.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/2935.png");background-image:url("https://twemoji.maxcdn.com/2/svg/2935.svg"),none}.em-arrow_heading_up{background-image:url("https://twemoji.maxcdn.com/2/72x72/2934.png")}.em-arrow_heading_up.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/2934.png");background-image:url("https://twemoji.maxcdn.com/2/svg/2934.svg"),none}.em-arrow_left{background-image:url("https://twemoji.maxcdn.com/2/72x72/2b05.png")}.em-arrow_left.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/2b05.png");background-image:url("https://twemoji.maxcdn.com/2/svg/2b05.svg"),none}.em-arrow_lower_left{background-image:url("https://twemoji.maxcdn.com/2/72x72/2199.png")}.em-arrow_lower_left.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/2199.png");background-image:url("https://twemoji.maxcdn.com/2/svg/2199.svg"),none}.em-arrow_lower_right{background-image:url("https://twemoji.maxcdn.com/2/72x72/2198.png")}.em-arrow_lower_right.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/2198.png");background-image:url("https://twemoji.maxcdn.com/2/svg/2198.svg"),none}.em-arrow_right{background-image:url("https://twemoji.maxcdn.com/2/72x72/27a1.png")}.em-arrow_right.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/27a1.png");background-image:url("https://twemoji.maxcdn.com/2/svg/27a1.svg"),none}.em-arrow_right_hook{background-image:url("https://twemoji.maxcdn.com/2/72x72/21aa.png")}.em-arrow_right_hook.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/21aa.png");background-image:url("https://twemoji.maxcdn.com/2/svg/21aa.svg"),none}.em-arrow_up{background-image:url("https://twemoji.maxcdn.com/2/72x72/2b06.png")}.em-arrow_up.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/2b06.png");background-image:url("https://twemoji.maxcdn.com/2/svg/2b06.svg"),none}.em-arrow_up_down{background-image:url("https://twemoji.maxcdn.com/2/72x72/2195.png")}.em-arrow_up_down.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/2195.png");background-image:url("https://twemoji.maxcdn.com/2/svg/2195.svg"),none}.em-arrow_up_small{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f53c.png")}.em-arrow_up_small.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f53c.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f53c.svg"),none}.em-arrow_upper_left{background-image:url("https://twemoji.maxcdn.com/2/72x72/2196.png")}.em-arrow_upper_left.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/2196.png");background-image:url("https://twemoji.maxcdn.com/2/svg/2196.svg"),none}.em-arrow_upper_right{background-image:url("https://twemoji.maxcdn.com/2/72x72/2197.png")}.em-arrow_upper_right.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/2197.png");background-image:url("https://twemoji.maxcdn.com/2/svg/2197.svg"),none}.em-arrows_clockwise{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f503.png")}.em-arrows_clockwise.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f503.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f503.svg"),none}.em-arrows_counterclockwise{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f504.png")}.em-arrows_counterclockwise.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f504.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f504.svg"),none}.em-art{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f3a8.png")}.em-art.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f3a8.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f3a8.svg"),none}.em-articulated_lorry{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f69b.png")}.em-articulated_lorry.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f69b.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f69b.svg"),none}.em-astonished{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f632.png")}.em-astonished.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f632.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f632.svg"),none}.em-athletic_shoe{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f45f.png")}.em-athletic_shoe.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f45f.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f45f.svg"),none}.em-atm{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f3e7.png")}.em-atm.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f3e7.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f3e7.svg"),none}.em-atom_symbol{background-image:url("https://twemoji.maxcdn.com/2/72x72/269b.png")}.em-atom_symbol.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/269b.png");background-image:url("https://twemoji.maxcdn.com/2/svg/269b.svg"),none}.em-avocado{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f951.png")}.em-avocado.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f951.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f951.svg"),none}.em-b{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f171.png")}.em-b.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f171.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f171.svg"),none}.em-baby{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f476.png")}.em-baby.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f476.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f476.svg"),none}.em-baby_bottle{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f37c.png")}.em-baby_bottle.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f37c.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f37c.svg"),none}.em-baby_chick{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f424.png")}.em-baby_chick.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f424.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f424.svg"),none}.em-baby_symbol{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f6bc.png")}.em-baby_symbol.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f6bc.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f6bc.svg"),none}.em-back{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f519.png")}.em-back.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f519.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f519.svg"),none}.em-bacon{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f953.png")}.em-bacon.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f953.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f953.svg"),none}.em-badminton_racquet_and_shuttlecock{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f3f8.png")}.em-badminton_racquet_and_shuttlecock.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f3f8.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f3f8.svg"),none}.em-baggage_claim{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f6c4.png")}.em-baggage_claim.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f6c4.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f6c4.svg"),none}.em-baguette_bread{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f956.png")}.em-baguette_bread.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f956.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f956.svg"),none}.em-balloon{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f388.png")}.em-balloon.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f388.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f388.svg"),none}.em-ballot_box_with_ballot{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f5f3.png")}.em-ballot_box_with_ballot.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f5f3.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f5f3.svg"),none}.em-ballot_box_with_check{background-image:url("https://twemoji.maxcdn.com/2/72x72/2611.png")}.em-ballot_box_with_check.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/2611.png");background-image:url("https://twemoji.maxcdn.com/2/svg/2611.svg"),none}.em-bamboo{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f38d.png")}.em-bamboo.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f38d.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f38d.svg"),none}.em-banana{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f34c.png")}.em-banana.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f34c.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f34c.svg"),none}.em-bangbang{background-image:url("https://twemoji.maxcdn.com/2/72x72/203c.png")}.em-bangbang.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/203c.png");background-image:url("https://twemoji.maxcdn.com/2/svg/203c.svg"),none}.em-bank{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f3e6.png")}.em-bank.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f3e6.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f3e6.svg"),none}.em-bar_chart{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f4ca.png")}.em-bar_chart.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f4ca.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f4ca.svg"),none}.em-barber{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f488.png")}.em-barber.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f488.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f488.svg"),none}.em-barely_sunny{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f325.png")}.em-barely_sunny.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f325.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f325.svg"),none}.em-baseball{background-image:url("https://twemoji.maxcdn.com/2/72x72/26be.png")}.em-baseball.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/26be.png");background-image:url("https://twemoji.maxcdn.com/2/svg/26be.svg"),none}.em-basketball{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f3c0.png")}.em-basketball.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f3c0.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f3c0.svg"),none}.em-bat{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f987.png")}.em-bat.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f987.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f987.svg"),none}.em-bath{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f6c0.png")}.em-bath.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f6c0.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f6c0.svg"),none}.em-bathtub{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f6c1.png")}.em-bathtub.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f6c1.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f6c1.svg"),none}.em-battery{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f50b.png")}.em-battery.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f50b.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f50b.svg"),none}.em-beach_with_umbrella{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f3d6.png")}.em-beach_with_umbrella.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f3d6.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f3d6.svg"),none}.em-bear{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f43b.png")}.em-bear.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f43b.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f43b.svg"),none}.em-bearded_person{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f9d4.png")}.em-bearded_person.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f9d4.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f9d4.svg"),none}.em-bed{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f6cf.png")}.em-bed.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f6cf.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f6cf.svg"),none}.em-bee{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f41d.png")}.em-bee.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f41d.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f41d.svg"),none}.em-beer{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f37a.png")}.em-beer.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f37a.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f37a.svg"),none}.em-beers{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f37b.png")}.em-beers.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f37b.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f37b.svg"),none}.em-beetle{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f41e.png")}.em-beetle.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f41e.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f41e.svg"),none}.em-beginner{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f530.png")}.em-beginner.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f530.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f530.svg"),none}.em-bell{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f514.png")}.em-bell.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f514.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f514.svg"),none}.em-bellhop_bell{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f6ce.png")}.em-bellhop_bell.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f6ce.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f6ce.svg"),none}.em-bento{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f371.png")}.em-bento.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f371.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f371.svg"),none}.em-bicyclist{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f6b4.png")}.em-bicyclist.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f6b4.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f6b4.svg"),none}.em-bike{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f6b2.png")}.em-bike.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f6b2.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f6b2.svg"),none}.em-bikini{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f459.png")}.em-bikini.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f459.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f459.svg"),none}.em-billed_cap{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f9e2.png")}.em-billed_cap.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f9e2.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f9e2.svg"),none}.em-biohazard_sign{background-image:url("https://twemoji.maxcdn.com/2/72x72/2623.png")}.em-biohazard_sign.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/2623.png");background-image:url("https://twemoji.maxcdn.com/2/svg/2623.svg"),none}.em-bird{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f426.png")}.em-bird.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f426.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f426.svg"),none}.em-birthday{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f382.png")}.em-birthday.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f382.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f382.svg"),none}.em-black_circle{background-image:url("https://twemoji.maxcdn.com/2/72x72/26ab.png")}.em-black_circle.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/26ab.png");background-image:url("https://twemoji.maxcdn.com/2/svg/26ab.svg"),none}.em-black_circle_for_record{background-image:url("https://twemoji.maxcdn.com/2/72x72/23fa.png")}.em-black_circle_for_record.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/23fa.png");background-image:url("https://twemoji.maxcdn.com/2/svg/23fa.svg"),none}.em-black_heart{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f5a4.png")}.em-black_heart.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f5a4.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f5a4.svg"),none}.em-black_joker{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f0cf.png")}.em-black_joker.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f0cf.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f0cf.svg"),none}.em-black_large_square{background-image:url("https://twemoji.maxcdn.com/2/72x72/2b1b.png")}.em-black_large_square.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/2b1b.png");background-image:url("https://twemoji.maxcdn.com/2/svg/2b1b.svg"),none}.em-black_left_pointing_double_triangle_with_vertical_bar{background-image:url("https://twemoji.maxcdn.com/2/72x72/23ee.png")}.em-black_left_pointing_double_triangle_with_vertical_bar.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/23ee.png");background-image:url("https://twemoji.maxcdn.com/2/svg/23ee.svg"),none}.em-black_medium_small_square{background-image:url("https://twemoji.maxcdn.com/2/72x72/25fe.png")}.em-black_medium_small_square.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/25fe.png");background-image:url("https://twemoji.maxcdn.com/2/svg/25fe.svg"),none}.em-black_medium_square{background-image:url("https://twemoji.maxcdn.com/2/72x72/25fc.png")}.em-black_medium_square.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/25fc.png");background-image:url("https://twemoji.maxcdn.com/2/svg/25fc.svg"),none}.em-black_nib{background-image:url("https://twemoji.maxcdn.com/2/72x72/2712.png")}.em-black_nib.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/2712.png");background-image:url("https://twemoji.maxcdn.com/2/svg/2712.svg"),none}.em-black_right_pointing_double_triangle_with_vertical_bar{background-image:url("https://twemoji.maxcdn.com/2/72x72/23ed.png")}.em-black_right_pointing_double_triangle_with_vertical_bar.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/23ed.png");background-image:url("https://twemoji.maxcdn.com/2/svg/23ed.svg"),none}.em-black_right_pointing_triangle_with_double_vertical_bar{background-image:url("https://twemoji.maxcdn.com/2/72x72/23ef.png")}.em-black_right_pointing_triangle_with_double_vertical_bar.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/23ef.png");background-image:url("https://twemoji.maxcdn.com/2/svg/23ef.svg"),none}.em-black_small_square{background-image:url("https://twemoji.maxcdn.com/2/72x72/25aa.png")}.em-black_small_square.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/25aa.png");background-image:url("https://twemoji.maxcdn.com/2/svg/25aa.svg"),none}.em-black_square_button{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f532.png")}.em-black_square_button.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f532.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f532.svg"),none}.em-black_square_for_stop{background-image:url("https://twemoji.maxcdn.com/2/72x72/23f9.png")}.em-black_square_for_stop.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/23f9.png");background-image:url("https://twemoji.maxcdn.com/2/svg/23f9.svg"),none}.em-blond-haired-man{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f471-200d-2642-fe0f.png")}.em-blond-haired-man.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f471-200d-2642-fe0f.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f471-200d-2642-fe0f.svg"),none}.em-blond-haired-woman{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f471-200d-2640-fe0f.png")}.em-blond-haired-woman.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f471-200d-2640-fe0f.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f471-200d-2640-fe0f.svg"),none}.em-blossom{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f33c.png")}.em-blossom.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f33c.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f33c.svg"),none}.em-blowfish{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f421.png")}.em-blowfish.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f421.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f421.svg"),none}.em-blue_book{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f4d8.png")}.em-blue_book.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f4d8.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f4d8.svg"),none}.em-blue_car{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f699.png")}.em-blue_car.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f699.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f699.svg"),none}.em-blue_heart{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f499.png")}.em-blue_heart.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f499.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f499.svg"),none}.em-blush{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f60a.png")}.em-blush.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f60a.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f60a.svg"),none}.em-boar{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f417.png")}.em-boar.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f417.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f417.svg"),none}.em-boat{background-image:url("https://twemoji.maxcdn.com/2/72x72/26f5.png")}.em-boat.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/26f5.png");background-image:url("https://twemoji.maxcdn.com/2/svg/26f5.svg"),none}.em-bomb{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f4a3.png")}.em-bomb.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f4a3.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f4a3.svg"),none}.em-book{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f4d6.png")}.em-book.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f4d6.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f4d6.svg"),none}.em-bookmark{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f516.png")}.em-bookmark.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f516.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f516.svg"),none}.em-bookmark_tabs{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f4d1.png")}.em-bookmark_tabs.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f4d1.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f4d1.svg"),none}.em-books{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f4da.png")}.em-books.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f4da.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f4da.svg"),none}.em-boom{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f4a5.png")}.em-boom.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f4a5.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f4a5.svg"),none}.em-boot{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f462.png")}.em-boot.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f462.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f462.svg"),none}.em-bouquet{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f490.png")}.em-bouquet.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f490.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f490.svg"),none}.em-bow{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f647.png")}.em-bow.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f647.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f647.svg"),none}.em-bow_and_arrow{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f3f9.png")}.em-bow_and_arrow.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f3f9.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f3f9.svg"),none}.em-bowl_with_spoon{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f963.png")}.em-bowl_with_spoon.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f963.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f963.svg"),none}.em-bowling{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f3b3.png")}.em-bowling.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f3b3.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f3b3.svg"),none}.em-boxing_glove{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f94a.png")}.em-boxing_glove.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f94a.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f94a.svg"),none}.em-boy{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f466.png")}.em-boy.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f466.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f466.svg"),none}.em-brain{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f9e0.png")}.em-brain.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f9e0.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f9e0.svg"),none}.em-bread{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f35e.png")}.em-bread.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f35e.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f35e.svg"),none}.em-breast-feeding{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f931.png")}.em-breast-feeding.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f931.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f931.svg"),none}.em-bride_with_veil{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f470.png")}.em-bride_with_veil.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f470.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f470.svg"),none}.em-bridge_at_night{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f309.png")}.em-bridge_at_night.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f309.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f309.svg"),none}.em-briefcase{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f4bc.png")}.em-briefcase.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f4bc.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f4bc.svg"),none}.em-broccoli{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f966.png")}.em-broccoli.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f966.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f966.svg"),none}.em-broken_heart{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f494.png")}.em-broken_heart.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f494.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f494.svg"),none}.em-bug{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f41b.png")}.em-bug.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f41b.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f41b.svg"),none}.em-building_construction{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f3d7.png")}.em-building_construction.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f3d7.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f3d7.svg"),none}.em-bulb{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f4a1.png")}.em-bulb.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f4a1.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f4a1.svg"),none}.em-bullettrain_front{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f685.png")}.em-bullettrain_front.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f685.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f685.svg"),none}.em-bullettrain_side{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f684.png")}.em-bullettrain_side.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f684.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f684.svg"),none}.em-burrito{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f32f.png")}.em-burrito.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f32f.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f32f.svg"),none}.em-bus{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f68c.png")}.em-bus.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f68c.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f68c.svg"),none}.em-busstop{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f68f.png")}.em-busstop.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f68f.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f68f.svg"),none}.em-bust_in_silhouette{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f464.png")}.em-bust_in_silhouette.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f464.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f464.svg"),none}.em-busts_in_silhouette{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f465.png")}.em-busts_in_silhouette.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f465.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f465.svg"),none}.em-butterfly{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f98b.png")}.em-butterfly.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f98b.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f98b.svg"),none}.em-cactus{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f335.png")}.em-cactus.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f335.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f335.svg"),none}.em-cake{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f370.png")}.em-cake.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f370.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f370.svg"),none}.em-calendar{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f4c6.png")}.em-calendar.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f4c6.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f4c6.svg"),none}.em-call_me_hand{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f919.png")}.em-call_me_hand.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f919.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f919.svg"),none}.em-calling{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f4f2.png")}.em-calling.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f4f2.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f4f2.svg"),none}.em-camel{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f42b.png")}.em-camel.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f42b.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f42b.svg"),none}.em-camera{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f4f7.png")}.em-camera.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f4f7.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f4f7.svg"),none}.em-camera_with_flash{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f4f8.png")}.em-camera_with_flash.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f4f8.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f4f8.svg"),none}.em-camping{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f3d5.png")}.em-camping.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f3d5.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f3d5.svg"),none}.em-cancer{background-image:url("https://twemoji.maxcdn.com/2/72x72/264b.png")}.em-cancer.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/264b.png");background-image:url("https://twemoji.maxcdn.com/2/svg/264b.svg"),none}.em-candle{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f56f.png")}.em-candle.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f56f.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f56f.svg"),none}.em-candy{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f36c.png")}.em-candy.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f36c.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f36c.svg"),none}.em-canned_food{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f96b.png")}.em-canned_food.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f96b.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f96b.svg"),none}.em-canoe{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f6f6.png")}.em-canoe.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f6f6.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f6f6.svg"),none}.em-capital_abcd{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f520.png")}.em-capital_abcd.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f520.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f520.svg"),none}.em-capricorn{background-image:url("https://twemoji.maxcdn.com/2/72x72/2651.png")}.em-capricorn.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/2651.png");background-image:url("https://twemoji.maxcdn.com/2/svg/2651.svg"),none}.em-car{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f697.png")}.em-car.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f697.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f697.svg"),none}.em-card_file_box{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f5c3.png")}.em-card_file_box.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f5c3.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f5c3.svg"),none}.em-card_index{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f4c7.png")}.em-card_index.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f4c7.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f4c7.svg"),none}.em-card_index_dividers{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f5c2.png")}.em-card_index_dividers.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f5c2.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f5c2.svg"),none}.em-carousel_horse{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f3a0.png")}.em-carousel_horse.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f3a0.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f3a0.svg"),none}.em-carrot{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f955.png")}.em-carrot.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f955.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f955.svg"),none}.em-cat{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f431.png")}.em-cat.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f431.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f431.svg"),none}.em-cat2{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f408.png")}.em-cat2.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f408.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f408.svg"),none}.em-cd{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f4bf.png")}.em-cd.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f4bf.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f4bf.svg"),none}.em-chains{background-image:url("https://twemoji.maxcdn.com/2/72x72/26d3.png")}.em-chains.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/26d3.png");background-image:url("https://twemoji.maxcdn.com/2/svg/26d3.svg"),none}.em-champagne{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f37e.png")}.em-champagne.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f37e.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f37e.svg"),none}.em-chart{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f4b9.png")}.em-chart.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f4b9.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f4b9.svg"),none}.em-chart_with_downwards_trend{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f4c9.png")}.em-chart_with_downwards_trend.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f4c9.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f4c9.svg"),none}.em-chart_with_upwards_trend{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f4c8.png")}.em-chart_with_upwards_trend.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f4c8.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f4c8.svg"),none}.em-checkered_flag{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f3c1.png")}.em-checkered_flag.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f3c1.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f3c1.svg"),none}.em-cheese_wedge{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f9c0.png")}.em-cheese_wedge.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f9c0.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f9c0.svg"),none}.em-cherries{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f352.png")}.em-cherries.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f352.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f352.svg"),none}.em-cherry_blossom{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f338.png")}.em-cherry_blossom.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f338.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f338.svg"),none}.em-chestnut{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f330.png")}.em-chestnut.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f330.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f330.svg"),none}.em-chicken{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f414.png")}.em-chicken.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f414.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f414.svg"),none}.em-child{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f9d2.png")}.em-child.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f9d2.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f9d2.svg"),none}.em-children_crossing{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f6b8.png")}.em-children_crossing.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f6b8.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f6b8.svg"),none}.em-chipmunk{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f43f.png")}.em-chipmunk.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f43f.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f43f.svg"),none}.em-chocolate_bar{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f36b.png")}.em-chocolate_bar.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f36b.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f36b.svg"),none}.em-chopsticks{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f962.png")}.em-chopsticks.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f962.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f962.svg"),none}.em-christmas_tree{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f384.png")}.em-christmas_tree.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f384.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f384.svg"),none}.em-church{background-image:url("https://twemoji.maxcdn.com/2/72x72/26ea.png")}.em-church.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/26ea.png");background-image:url("https://twemoji.maxcdn.com/2/svg/26ea.svg"),none}.em-cinema{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f3a6.png")}.em-cinema.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f3a6.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f3a6.svg"),none}.em-circus_tent{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f3aa.png")}.em-circus_tent.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f3aa.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f3aa.svg"),none}.em-city_sunrise{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f307.png")}.em-city_sunrise.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f307.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f307.svg"),none}.em-city_sunset{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f306.png")}.em-city_sunset.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f306.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f306.svg"),none}.em-cityscape{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f3d9.png")}.em-cityscape.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f3d9.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f3d9.svg"),none}.em-cl{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f191.png")}.em-cl.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f191.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f191.svg"),none}.em-clap{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f44f.png")}.em-clap.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f44f.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f44f.svg"),none}.em-clapper{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f3ac.png")}.em-clapper.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f3ac.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f3ac.svg"),none}.em-classical_building{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f3db.png")}.em-classical_building.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f3db.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f3db.svg"),none}.em-clinking_glasses{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f942.png")}.em-clinking_glasses.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f942.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f942.svg"),none}.em-clipboard{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f4cb.png")}.em-clipboard.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f4cb.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f4cb.svg"),none}.em-clock1{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f550.png")}.em-clock1.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f550.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f550.svg"),none}.em-clock10{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f559.png")}.em-clock10.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f559.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f559.svg"),none}.em-clock1030{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f565.png")}.em-clock1030.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f565.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f565.svg"),none}.em-clock11{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f55a.png")}.em-clock11.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f55a.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f55a.svg"),none}.em-clock1130{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f566.png")}.em-clock1130.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f566.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f566.svg"),none}.em-clock12{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f55b.png")}.em-clock12.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f55b.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f55b.svg"),none}.em-clock1230{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f567.png")}.em-clock1230.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f567.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f567.svg"),none}.em-clock130{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f55c.png")}.em-clock130.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f55c.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f55c.svg"),none}.em-clock2{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f551.png")}.em-clock2.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f551.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f551.svg"),none}.em-clock230{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f55d.png")}.em-clock230.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f55d.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f55d.svg"),none}.em-clock3{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f552.png")}.em-clock3.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f552.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f552.svg"),none}.em-clock330{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f55e.png")}.em-clock330.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f55e.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f55e.svg"),none}.em-clock4{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f553.png")}.em-clock4.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f553.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f553.svg"),none}.em-clock430{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f55f.png")}.em-clock430.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f55f.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f55f.svg"),none}.em-clock5{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f554.png")}.em-clock5.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f554.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f554.svg"),none}.em-clock530{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f560.png")}.em-clock530.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f560.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f560.svg"),none}.em-clock6{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f555.png")}.em-clock6.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f555.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f555.svg"),none}.em-clock630{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f561.png")}.em-clock630.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f561.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f561.svg"),none}.em-clock7{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f556.png")}.em-clock7.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f556.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f556.svg"),none}.em-clock730{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f562.png")}.em-clock730.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f562.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f562.svg"),none}.em-clock8{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f557.png")}.em-clock8.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f557.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f557.svg"),none}.em-clock830{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f563.png")}.em-clock830.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f563.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f563.svg"),none}.em-clock9{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f558.png")}.em-clock9.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f558.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f558.svg"),none}.em-clock930{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f564.png")}.em-clock930.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f564.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f564.svg"),none}.em-closed_book{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f4d5.png")}.em-closed_book.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f4d5.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f4d5.svg"),none}.em-closed_lock_with_key{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f510.png")}.em-closed_lock_with_key.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f510.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f510.svg"),none}.em-closed_umbrella{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f302.png")}.em-closed_umbrella.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f302.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f302.svg"),none}.em-cloud{background-image:url("https://twemoji.maxcdn.com/2/72x72/2601.png")}.em-cloud.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/2601.png");background-image:url("https://twemoji.maxcdn.com/2/svg/2601.svg"),none}.em-clown_face{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f921.png")}.em-clown_face.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f921.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f921.svg"),none}.em-clubs{background-image:url("https://twemoji.maxcdn.com/2/72x72/2663.png")}.em-clubs.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/2663.png");background-image:url("https://twemoji.maxcdn.com/2/svg/2663.svg"),none}.em-cn{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1e8-1f1f3.png")}.em-cn.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1e8-1f1f3.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1e8-1f1f3.svg"),none}.em-coat{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f9e5.png")}.em-coat.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f9e5.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f9e5.svg"),none}.em-cocktail{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f378.png")}.em-cocktail.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f378.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f378.svg"),none}.em-coconut{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f965.png")}.em-coconut.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f965.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f965.svg"),none}.em-coffee{background-image:url("https://twemoji.maxcdn.com/2/72x72/2615.png")}.em-coffee.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/2615.png");background-image:url("https://twemoji.maxcdn.com/2/svg/2615.svg"),none}.em-coffin{background-image:url("https://twemoji.maxcdn.com/2/72x72/26b0.png")}.em-coffin.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/26b0.png");background-image:url("https://twemoji.maxcdn.com/2/svg/26b0.svg"),none}.em-cold_sweat{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f630.png")}.em-cold_sweat.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f630.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f630.svg"),none}.em-comet{background-image:url("https://twemoji.maxcdn.com/2/72x72/2604.png")}.em-comet.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/2604.png");background-image:url("https://twemoji.maxcdn.com/2/svg/2604.svg"),none}.em-compression{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f5dc.png")}.em-compression.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f5dc.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f5dc.svg"),none}.em-computer{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f4bb.png")}.em-computer.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f4bb.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f4bb.svg"),none}.em-confetti_ball{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f38a.png")}.em-confetti_ball.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f38a.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f38a.svg"),none}.em-confounded{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f616.png")}.em-confounded.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f616.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f616.svg"),none}.em-confused{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f615.png")}.em-confused.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f615.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f615.svg"),none}.em-congratulations{background-image:url("https://twemoji.maxcdn.com/2/72x72/3297.png")}.em-congratulations.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/3297.png");background-image:url("https://twemoji.maxcdn.com/2/svg/3297.svg"),none}.em-construction{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f6a7.png")}.em-construction.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f6a7.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f6a7.svg"),none}.em-construction_worker{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f477.png")}.em-construction_worker.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f477.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f477.svg"),none}.em-control_knobs{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f39b.png")}.em-control_knobs.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f39b.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f39b.svg"),none}.em-convenience_store{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f3ea.png")}.em-convenience_store.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f3ea.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f3ea.svg"),none}.em-cookie{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f36a.png")}.em-cookie.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f36a.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f36a.svg"),none}.em-cool{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f192.png")}.em-cool.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f192.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f192.svg"),none}.em-cop{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f46e.png")}.em-cop.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f46e.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f46e.svg"),none}.em-copyright{background-image:url("https://twemoji.maxcdn.com/2/72x72/a9.png")}.em-copyright.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/a9.png");background-image:url("https://twemoji.maxcdn.com/2/svg/a9.svg"),none}.em-corn{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f33d.png")}.em-corn.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f33d.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f33d.svg"),none}.em-couch_and_lamp{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f6cb.png")}.em-couch_and_lamp.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f6cb.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f6cb.svg"),none}.em-couple{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f46b.png")}.em-couple.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f46b.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f46b.svg"),none}.em-couple_with_heart{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f491.png")}.em-couple_with_heart.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f491.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f491.svg"),none}.em-couplekiss{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f48f.png")}.em-couplekiss.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f48f.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f48f.svg"),none}.em-cow{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f42e.png")}.em-cow.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f42e.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f42e.svg"),none}.em-cow2{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f404.png")}.em-cow2.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f404.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f404.svg"),none}.em-crab{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f980.png")}.em-crab.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f980.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f980.svg"),none}.em-credit_card{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f4b3.png")}.em-credit_card.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f4b3.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f4b3.svg"),none}.em-crescent_moon{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f319.png")}.em-crescent_moon.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f319.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f319.svg"),none}.em-cricket{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f997.png")}.em-cricket.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f997.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f997.svg"),none}.em-cricket_bat_and_ball{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f3cf.png")}.em-cricket_bat_and_ball.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f3cf.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f3cf.svg"),none}.em-crocodile{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f40a.png")}.em-crocodile.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f40a.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f40a.svg"),none}.em-croissant{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f950.png")}.em-croissant.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f950.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f950.svg"),none}.em-crossed_flags{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f38c.png")}.em-crossed_flags.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f38c.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f38c.svg"),none}.em-crossed_swords{background-image:url("https://twemoji.maxcdn.com/2/72x72/2694.png")}.em-crossed_swords.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/2694.png");background-image:url("https://twemoji.maxcdn.com/2/svg/2694.svg"),none}.em-crown{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f451.png")}.em-crown.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f451.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f451.svg"),none}.em-cry{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f622.png")}.em-cry.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f622.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f622.svg"),none}.em-crying_cat_face{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f63f.png")}.em-crying_cat_face.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f63f.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f63f.svg"),none}.em-crystal_ball{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f52e.png")}.em-crystal_ball.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f52e.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f52e.svg"),none}.em-cucumber{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f952.png")}.em-cucumber.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f952.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f952.svg"),none}.em-cup_with_straw{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f964.png")}.em-cup_with_straw.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f964.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f964.svg"),none}.em-cupid{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f498.png")}.em-cupid.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f498.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f498.svg"),none}.em-curling_stone{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f94c.png")}.em-curling_stone.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f94c.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f94c.svg"),none}.em-curly_loop{background-image:url("https://twemoji.maxcdn.com/2/72x72/27b0.png")}.em-curly_loop.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/27b0.png");background-image:url("https://twemoji.maxcdn.com/2/svg/27b0.svg"),none}.em-currency_exchange{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f4b1.png")}.em-currency_exchange.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f4b1.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f4b1.svg"),none}.em-curry{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f35b.png")}.em-curry.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f35b.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f35b.svg"),none}.em-custard{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f36e.png")}.em-custard.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f36e.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f36e.svg"),none}.em-customs{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f6c3.png")}.em-customs.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f6c3.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f6c3.svg"),none}.em-cut_of_meat{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f969.png")}.em-cut_of_meat.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f969.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f969.svg"),none}.em-cyclone{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f300.png")}.em-cyclone.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f300.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f300.svg"),none}.em-dagger_knife{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f5e1.png")}.em-dagger_knife.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f5e1.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f5e1.svg"),none}.em-dancer{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f483.png")}.em-dancer.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f483.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f483.svg"),none}.em-dancers{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f46f.png")}.em-dancers.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f46f.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f46f.svg"),none}.em-dango{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f361.png")}.em-dango.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f361.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f361.svg"),none}.em-dark_sunglasses{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f576.png")}.em-dark_sunglasses.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f576.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f576.svg"),none}.em-dart{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f3af.png")}.em-dart.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f3af.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f3af.svg"),none}.em-dash{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f4a8.png")}.em-dash.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f4a8.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f4a8.svg"),none}.em-date{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f4c5.png")}.em-date.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f4c5.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f4c5.svg"),none}.em-de{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1e9-1f1ea.png")}.em-de.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1e9-1f1ea.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1e9-1f1ea.svg"),none}.em-deciduous_tree{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f333.png")}.em-deciduous_tree.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f333.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f333.svg"),none}.em-deer{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f98c.png")}.em-deer.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f98c.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f98c.svg"),none}.em-department_store{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f3ec.png")}.em-department_store.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f3ec.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f3ec.svg"),none}.em-derelict_house_building{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f3da.png")}.em-derelict_house_building.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f3da.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f3da.svg"),none}.em-desert{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f3dc.png")}.em-desert.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f3dc.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f3dc.svg"),none}.em-desert_island{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f3dd.png")}.em-desert_island.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f3dd.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f3dd.svg"),none}.em-desktop_computer{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f5a5.png")}.em-desktop_computer.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f5a5.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f5a5.svg"),none}.em-diamond_shape_with_a_dot_inside{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f4a0.png")}.em-diamond_shape_with_a_dot_inside.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f4a0.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f4a0.svg"),none}.em-diamonds{background-image:url("https://twemoji.maxcdn.com/2/72x72/2666.png")}.em-diamonds.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/2666.png");background-image:url("https://twemoji.maxcdn.com/2/svg/2666.svg"),none}.em-disappointed{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f61e.png")}.em-disappointed.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f61e.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f61e.svg"),none}.em-disappointed_relieved{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f625.png")}.em-disappointed_relieved.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f625.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f625.svg"),none}.em-dizzy{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f4ab.png")}.em-dizzy.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f4ab.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f4ab.svg"),none}.em-dizzy_face{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f635.png")}.em-dizzy_face.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f635.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f635.svg"),none}.em-do_not_litter{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f6af.png")}.em-do_not_litter.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f6af.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f6af.svg"),none}.em-dog{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f436.png")}.em-dog.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f436.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f436.svg"),none}.em-dog2{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f415.png")}.em-dog2.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f415.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f415.svg"),none}.em-dollar{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f4b5.png")}.em-dollar.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f4b5.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f4b5.svg"),none}.em-dolls{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f38e.png")}.em-dolls.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f38e.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f38e.svg"),none}.em-dolphin{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f42c.png")}.em-dolphin.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f42c.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f42c.svg"),none}.em-door{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f6aa.png")}.em-door.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f6aa.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f6aa.svg"),none}.em-double_vertical_bar{background-image:url("https://twemoji.maxcdn.com/2/72x72/23f8.png")}.em-double_vertical_bar.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/23f8.png");background-image:url("https://twemoji.maxcdn.com/2/svg/23f8.svg"),none}.em-doughnut{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f369.png")}.em-doughnut.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f369.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f369.svg"),none}.em-dove_of_peace{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f54a.png")}.em-dove_of_peace.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f54a.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f54a.svg"),none}.em-dragon{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f409.png")}.em-dragon.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f409.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f409.svg"),none}.em-dragon_face{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f432.png")}.em-dragon_face.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f432.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f432.svg"),none}.em-dress{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f457.png")}.em-dress.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f457.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f457.svg"),none}.em-dromedary_camel{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f42a.png")}.em-dromedary_camel.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f42a.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f42a.svg"),none}.em-drooling_face{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f924.png")}.em-drooling_face.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f924.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f924.svg"),none}.em-droplet{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f4a7.png")}.em-droplet.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f4a7.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f4a7.svg"),none}.em-drum_with_drumsticks{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f941.png")}.em-drum_with_drumsticks.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f941.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f941.svg"),none}.em-duck{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f986.png")}.em-duck.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f986.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f986.svg"),none}.em-dumpling{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f95f.png")}.em-dumpling.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f95f.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f95f.svg"),none}.em-dvd{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f4c0.png")}.em-dvd.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f4c0.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f4c0.svg"),none}.em-e-mail{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f4e7.png")}.em-e-mail.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f4e7.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f4e7.svg"),none}.em-eagle{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f985.png")}.em-eagle.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f985.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f985.svg"),none}.em-ear{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f442.png")}.em-ear.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f442.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f442.svg"),none}.em-ear_of_rice{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f33e.png")}.em-ear_of_rice.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f33e.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f33e.svg"),none}.em-earth_africa{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f30d.png")}.em-earth_africa.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f30d.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f30d.svg"),none}.em-earth_americas{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f30e.png")}.em-earth_americas.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f30e.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f30e.svg"),none}.em-earth_asia{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f30f.png")}.em-earth_asia.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f30f.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f30f.svg"),none}.em-egg{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f95a.png")}.em-egg.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f95a.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f95a.svg"),none}.em-eggplant{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f346.png")}.em-eggplant.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f346.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f346.svg"),none}.em-eight{background-image:url("https://twemoji.maxcdn.com/2/72x72/38-20e3.png")}.em-eight.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/38-20e3.png");background-image:url("https://twemoji.maxcdn.com/2/svg/38-20e3.svg"),none}.em-eight_pointed_black_star{background-image:url("https://twemoji.maxcdn.com/2/72x72/2734.png")}.em-eight_pointed_black_star.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/2734.png");background-image:url("https://twemoji.maxcdn.com/2/svg/2734.svg"),none}.em-eight_spoked_asterisk{background-image:url("https://twemoji.maxcdn.com/2/72x72/2733.png")}.em-eight_spoked_asterisk.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/2733.png");background-image:url("https://twemoji.maxcdn.com/2/svg/2733.svg"),none}.em-eject{background-image:url("https://twemoji.maxcdn.com/2/72x72/23cf.png")}.em-eject.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/23cf.png");background-image:url("https://twemoji.maxcdn.com/2/svg/23cf.svg"),none}.em-electric_plug{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f50c.png")}.em-electric_plug.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f50c.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f50c.svg"),none}.em-elephant{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f418.png")}.em-elephant.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f418.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f418.svg"),none}.em-elf{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f9dd.png")}.em-elf.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f9dd.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f9dd.svg"),none}.em-email{background-image:url("https://twemoji.maxcdn.com/2/72x72/2709.png")}.em-email.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/2709.png");background-image:url("https://twemoji.maxcdn.com/2/svg/2709.svg"),none}.em-end{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f51a.png")}.em-end.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f51a.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f51a.svg"),none}.em-envelope_with_arrow{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f4e9.png")}.em-envelope_with_arrow.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f4e9.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f4e9.svg"),none}.em-es{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1ea-1f1f8.png")}.em-es.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1ea-1f1f8.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1ea-1f1f8.svg"),none}.em-euro{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f4b6.png")}.em-euro.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f4b6.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f4b6.svg"),none}.em-european_castle{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f3f0.png")}.em-european_castle.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f3f0.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f3f0.svg"),none}.em-european_post_office{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f3e4.png")}.em-european_post_office.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f3e4.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f3e4.svg"),none}.em-evergreen_tree{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f332.png")}.em-evergreen_tree.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f332.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f332.svg"),none}.em-exclamation{background-image:url("https://twemoji.maxcdn.com/2/72x72/2757.png")}.em-exclamation.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/2757.png");background-image:url("https://twemoji.maxcdn.com/2/svg/2757.svg"),none}.em-expressionless{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f611.png")}.em-expressionless.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f611.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f611.svg"),none}.em-eye{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f441.png")}.em-eye.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f441.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f441.svg"),none}.em-eyeglasses{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f453.png")}.em-eyeglasses.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f453.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f453.svg"),none}.em-eyes{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f440.png")}.em-eyes.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f440.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f440.svg"),none}.em-face_palm{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f926.png")}.em-face_palm.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f926.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f926.svg"),none}.em-face_with_cowboy_hat{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f920.png")}.em-face_with_cowboy_hat.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f920.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f920.svg"),none}.em-face_with_finger_covering_closed_lips{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f92b.png")}.em-face_with_finger_covering_closed_lips.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f92b.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f92b.svg"),none}.em-face_with_head_bandage{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f915.png")}.em-face_with_head_bandage.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f915.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f915.svg"),none}.em-face_with_monocle{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f9d0.png")}.em-face_with_monocle.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f9d0.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f9d0.svg"),none}.em-face_with_one_eyebrow_raised{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f928.png")}.em-face_with_one_eyebrow_raised.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f928.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f928.svg"),none}.em-face_with_open_mouth_vomiting{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f92e.png")}.em-face_with_open_mouth_vomiting.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f92e.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f92e.svg"),none}.em-face_with_rolling_eyes{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f644.png")}.em-face_with_rolling_eyes.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f644.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f644.svg"),none}.em-face_with_thermometer{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f912.png")}.em-face_with_thermometer.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f912.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f912.svg"),none}.em-facepunch{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f44a.png")}.em-facepunch.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f44a.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f44a.svg"),none}.em-factory{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f3ed.png")}.em-factory.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f3ed.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f3ed.svg"),none}.em-fairy{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f9da.png")}.em-fairy.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f9da.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f9da.svg"),none}.em-fallen_leaf{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f342.png")}.em-fallen_leaf.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f342.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f342.svg"),none}.em-family{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f46a.png")}.em-family.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f46a.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f46a.svg"),none}.em-fast_forward{background-image:url("https://twemoji.maxcdn.com/2/72x72/23e9.png")}.em-fast_forward.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/23e9.png");background-image:url("https://twemoji.maxcdn.com/2/svg/23e9.svg"),none}.em-fax{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f4e0.png")}.em-fax.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f4e0.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f4e0.svg"),none}.em-fearful{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f628.png")}.em-fearful.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f628.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f628.svg"),none}.em-feet{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f43e.png")}.em-feet.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f43e.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f43e.svg"),none}.em-female-artist{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f469-200d-1f3a8.png")}.em-female-artist.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f469-200d-1f3a8.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f469-200d-1f3a8.svg"),none}.em-female-astronaut{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f469-200d-1f680.png")}.em-female-astronaut.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f469-200d-1f680.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f469-200d-1f680.svg"),none}.em-female-construction-worker{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f477-200d-2640-fe0f.png")}.em-female-construction-worker.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f477-200d-2640-fe0f.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f477-200d-2640-fe0f.svg"),none}.em-female-cook{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f469-200d-1f373.png")}.em-female-cook.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f469-200d-1f373.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f469-200d-1f373.svg"),none}.em-female-detective{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f575-fe0f-200d-2640-fe0f.png")}.em-female-detective.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f575-fe0f-200d-2640-fe0f.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f575-fe0f-200d-2640-fe0f.svg"),none}.em-female-doctor{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f469-200d-2695-fe0f.png")}.em-female-doctor.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f469-200d-2695-fe0f.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f469-200d-2695-fe0f.svg"),none}.em-female-factory-worker{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f469-200d-1f3ed.png")}.em-female-factory-worker.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f469-200d-1f3ed.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f469-200d-1f3ed.svg"),none}.em-female-farmer{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f469-200d-1f33e.png")}.em-female-farmer.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f469-200d-1f33e.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f469-200d-1f33e.svg"),none}.em-female-firefighter{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f469-200d-1f692.png")}.em-female-firefighter.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f469-200d-1f692.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f469-200d-1f692.svg"),none}.em-female-guard{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f482-200d-2640-fe0f.png")}.em-female-guard.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f482-200d-2640-fe0f.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f482-200d-2640-fe0f.svg"),none}.em-female-judge{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f469-200d-2696-fe0f.png")}.em-female-judge.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f469-200d-2696-fe0f.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f469-200d-2696-fe0f.svg"),none}.em-female-mechanic{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f469-200d-1f527.png")}.em-female-mechanic.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f469-200d-1f527.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f469-200d-1f527.svg"),none}.em-female-office-worker{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f469-200d-1f4bc.png")}.em-female-office-worker.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f469-200d-1f4bc.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f469-200d-1f4bc.svg"),none}.em-female-pilot{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f469-200d-2708-fe0f.png")}.em-female-pilot.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f469-200d-2708-fe0f.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f469-200d-2708-fe0f.svg"),none}.em-female-police-officer{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f46e-200d-2640-fe0f.png")}.em-female-police-officer.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f46e-200d-2640-fe0f.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f46e-200d-2640-fe0f.svg"),none}.em-female-scientist{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f469-200d-1f52c.png")}.em-female-scientist.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f469-200d-1f52c.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f469-200d-1f52c.svg"),none}.em-female-singer{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f469-200d-1f3a4.png")}.em-female-singer.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f469-200d-1f3a4.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f469-200d-1f3a4.svg"),none}.em-female-student{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f469-200d-1f393.png")}.em-female-student.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f469-200d-1f393.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f469-200d-1f393.svg"),none}.em-female-teacher{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f469-200d-1f3eb.png")}.em-female-teacher.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f469-200d-1f3eb.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f469-200d-1f3eb.svg"),none}.em-female-technologist{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f469-200d-1f4bb.png")}.em-female-technologist.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f469-200d-1f4bb.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f469-200d-1f4bb.svg"),none}.em-female_elf{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f9dd-200d-2640-fe0f.png")}.em-female_elf.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f9dd-200d-2640-fe0f.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f9dd-200d-2640-fe0f.svg"),none}.em-female_fairy{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f9da-200d-2640-fe0f.png")}.em-female_fairy.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f9da-200d-2640-fe0f.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f9da-200d-2640-fe0f.svg"),none}.em-female_genie{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f9de-200d-2640-fe0f.png")}.em-female_genie.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f9de-200d-2640-fe0f.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f9de-200d-2640-fe0f.svg"),none}.em-female_mage{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f9d9-200d-2640-fe0f.png")}.em-female_mage.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f9d9-200d-2640-fe0f.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f9d9-200d-2640-fe0f.svg"),none}.em-female_sign{background-image:url("https://twemoji.maxcdn.com/2/72x72/2640.png")}.em-female_sign.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/2640.png");background-image:url("https://twemoji.maxcdn.com/2/svg/2640.svg"),none}.em-female_vampire{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f9db-200d-2640-fe0f.png")}.em-female_vampire.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f9db-200d-2640-fe0f.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f9db-200d-2640-fe0f.svg"),none}.em-female_zombie{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f9df-200d-2640-fe0f.png")}.em-female_zombie.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f9df-200d-2640-fe0f.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f9df-200d-2640-fe0f.svg"),none}.em-fencer{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f93a.png")}.em-fencer.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f93a.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f93a.svg"),none}.em-ferris_wheel{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f3a1.png")}.em-ferris_wheel.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f3a1.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f3a1.svg"),none}.em-ferry{background-image:url("https://twemoji.maxcdn.com/2/72x72/26f4.png")}.em-ferry.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/26f4.png");background-image:url("https://twemoji.maxcdn.com/2/svg/26f4.svg"),none}.em-field_hockey_stick_and_ball{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f3d1.png")}.em-field_hockey_stick_and_ball.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f3d1.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f3d1.svg"),none}.em-file_cabinet{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f5c4.png")}.em-file_cabinet.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f5c4.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f5c4.svg"),none}.em-file_folder{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f4c1.png")}.em-file_folder.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f4c1.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f4c1.svg"),none}.em-film_frames{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f39e.png")}.em-film_frames.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f39e.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f39e.svg"),none}.em-film_projector{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f4fd.png")}.em-film_projector.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f4fd.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f4fd.svg"),none}.em-fire{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f525.png")}.em-fire.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f525.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f525.svg"),none}.em-fire_engine{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f692.png")}.em-fire_engine.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f692.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f692.svg"),none}.em-fireworks{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f386.png")}.em-fireworks.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f386.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f386.svg"),none}.em-first_place_medal{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f947.png")}.em-first_place_medal.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f947.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f947.svg"),none}.em-first_quarter_moon{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f313.png")}.em-first_quarter_moon.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f313.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f313.svg"),none}.em-first_quarter_moon_with_face{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f31b.png")}.em-first_quarter_moon_with_face.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f31b.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f31b.svg"),none}.em-fish{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f41f.png")}.em-fish.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f41f.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f41f.svg"),none}.em-fish_cake{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f365.png")}.em-fish_cake.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f365.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f365.svg"),none}.em-fishing_pole_and_fish{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f3a3.png")}.em-fishing_pole_and_fish.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f3a3.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f3a3.svg"),none}.em-fist{background-image:url("https://twemoji.maxcdn.com/2/72x72/270a.png")}.em-fist.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/270a.png");background-image:url("https://twemoji.maxcdn.com/2/svg/270a.svg"),none}.em-five{background-image:url("https://twemoji.maxcdn.com/2/72x72/35-20e3.png")}.em-five.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/35-20e3.png");background-image:url("https://twemoji.maxcdn.com/2/svg/35-20e3.svg"),none}.em-flag-ac{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1e6-1f1e8.png")}.em-flag-ac.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1e6-1f1e8.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1e6-1f1e8.svg"),none}.em-flag-ad{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1e6-1f1e9.png")}.em-flag-ad.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1e6-1f1e9.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1e6-1f1e9.svg"),none}.em-flag-ae{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1e6-1f1ea.png")}.em-flag-ae.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1e6-1f1ea.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1e6-1f1ea.svg"),none}.em-flag-af{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1e6-1f1eb.png")}.em-flag-af.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1e6-1f1eb.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1e6-1f1eb.svg"),none}.em-flag-ag{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1e6-1f1ec.png")}.em-flag-ag.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1e6-1f1ec.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1e6-1f1ec.svg"),none}.em-flag-ai{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1e6-1f1ee.png")}.em-flag-ai.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1e6-1f1ee.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1e6-1f1ee.svg"),none}.em-flag-al{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1e6-1f1f1.png")}.em-flag-al.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1e6-1f1f1.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1e6-1f1f1.svg"),none}.em-flag-am{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1e6-1f1f2.png")}.em-flag-am.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1e6-1f1f2.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1e6-1f1f2.svg"),none}.em-flag-ao{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1e6-1f1f4.png")}.em-flag-ao.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1e6-1f1f4.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1e6-1f1f4.svg"),none}.em-flag-aq{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1e6-1f1f6.png")}.em-flag-aq.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1e6-1f1f6.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1e6-1f1f6.svg"),none}.em-flag-ar{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1e6-1f1f7.png")}.em-flag-ar.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1e6-1f1f7.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1e6-1f1f7.svg"),none}.em-flag-as{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1e6-1f1f8.png")}.em-flag-as.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1e6-1f1f8.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1e6-1f1f8.svg"),none}.em-flag-at{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1e6-1f1f9.png")}.em-flag-at.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1e6-1f1f9.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1e6-1f1f9.svg"),none}.em-flag-au{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1e6-1f1fa.png")}.em-flag-au.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1e6-1f1fa.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1e6-1f1fa.svg"),none}.em-flag-aw{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1e6-1f1fc.png")}.em-flag-aw.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1e6-1f1fc.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1e6-1f1fc.svg"),none}.em-flag-ax{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1e6-1f1fd.png")}.em-flag-ax.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1e6-1f1fd.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1e6-1f1fd.svg"),none}.em-flag-az{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1e6-1f1ff.png")}.em-flag-az.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1e6-1f1ff.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1e6-1f1ff.svg"),none}.em-flag-ba{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1e7-1f1e6.png")}.em-flag-ba.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1e7-1f1e6.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1e7-1f1e6.svg"),none}.em-flag-bb{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1e7-1f1e7.png")}.em-flag-bb.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1e7-1f1e7.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1e7-1f1e7.svg"),none}.em-flag-bd{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1e7-1f1e9.png")}.em-flag-bd.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1e7-1f1e9.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1e7-1f1e9.svg"),none}.em-flag-be{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1e7-1f1ea.png")}.em-flag-be.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1e7-1f1ea.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1e7-1f1ea.svg"),none}.em-flag-bf{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1e7-1f1eb.png")}.em-flag-bf.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1e7-1f1eb.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1e7-1f1eb.svg"),none}.em-flag-bg{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1e7-1f1ec.png")}.em-flag-bg.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1e7-1f1ec.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1e7-1f1ec.svg"),none}.em-flag-bh{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1e7-1f1ed.png")}.em-flag-bh.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1e7-1f1ed.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1e7-1f1ed.svg"),none}.em-flag-bi{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1e7-1f1ee.png")}.em-flag-bi.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1e7-1f1ee.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1e7-1f1ee.svg"),none}.em-flag-bj{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1e7-1f1ef.png")}.em-flag-bj.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1e7-1f1ef.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1e7-1f1ef.svg"),none}.em-flag-bl{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1e7-1f1f1.png")}.em-flag-bl.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1e7-1f1f1.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1e7-1f1f1.svg"),none}.em-flag-bm{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1e7-1f1f2.png")}.em-flag-bm.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1e7-1f1f2.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1e7-1f1f2.svg"),none}.em-flag-bn{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1e7-1f1f3.png")}.em-flag-bn.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1e7-1f1f3.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1e7-1f1f3.svg"),none}.em-flag-bo{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1e7-1f1f4.png")}.em-flag-bo.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1e7-1f1f4.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1e7-1f1f4.svg"),none}.em-flag-bq{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1e7-1f1f6.png")}.em-flag-bq.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1e7-1f1f6.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1e7-1f1f6.svg"),none}.em-flag-br{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1e7-1f1f7.png")}.em-flag-br.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1e7-1f1f7.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1e7-1f1f7.svg"),none}.em-flag-bs{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1e7-1f1f8.png")}.em-flag-bs.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1e7-1f1f8.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1e7-1f1f8.svg"),none}.em-flag-bt{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1e7-1f1f9.png")}.em-flag-bt.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1e7-1f1f9.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1e7-1f1f9.svg"),none}.em-flag-bv{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1e7-1f1fb.png")}.em-flag-bv.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1e7-1f1fb.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1e7-1f1fb.svg"),none}.em-flag-bw{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1e7-1f1fc.png")}.em-flag-bw.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1e7-1f1fc.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1e7-1f1fc.svg"),none}.em-flag-by{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1e7-1f1fe.png")}.em-flag-by.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1e7-1f1fe.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1e7-1f1fe.svg"),none}.em-flag-bz{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1e7-1f1ff.png")}.em-flag-bz.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1e7-1f1ff.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1e7-1f1ff.svg"),none}.em-flag-ca{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1e8-1f1e6.png")}.em-flag-ca.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1e8-1f1e6.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1e8-1f1e6.svg"),none}.em-flag-cc{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1e8-1f1e8.png")}.em-flag-cc.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1e8-1f1e8.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1e8-1f1e8.svg"),none}.em-flag-cd{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1e8-1f1e9.png")}.em-flag-cd.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1e8-1f1e9.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1e8-1f1e9.svg"),none}.em-flag-cf{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1e8-1f1eb.png")}.em-flag-cf.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1e8-1f1eb.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1e8-1f1eb.svg"),none}.em-flag-cg{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1e8-1f1ec.png")}.em-flag-cg.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1e8-1f1ec.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1e8-1f1ec.svg"),none}.em-flag-ch{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1e8-1f1ed.png")}.em-flag-ch.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1e8-1f1ed.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1e8-1f1ed.svg"),none}.em-flag-ci{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1e8-1f1ee.png")}.em-flag-ci.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1e8-1f1ee.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1e8-1f1ee.svg"),none}.em-flag-ck{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1e8-1f1f0.png")}.em-flag-ck.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1e8-1f1f0.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1e8-1f1f0.svg"),none}.em-flag-cl{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1e8-1f1f1.png")}.em-flag-cl.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1e8-1f1f1.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1e8-1f1f1.svg"),none}.em-flag-cm{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1e8-1f1f2.png")}.em-flag-cm.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1e8-1f1f2.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1e8-1f1f2.svg"),none}.em-flag-co{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1e8-1f1f4.png")}.em-flag-co.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1e8-1f1f4.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1e8-1f1f4.svg"),none}.em-flag-cp{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1e8-1f1f5.png")}.em-flag-cp.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1e8-1f1f5.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1e8-1f1f5.svg"),none}.em-flag-cr{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1e8-1f1f7.png")}.em-flag-cr.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1e8-1f1f7.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1e8-1f1f7.svg"),none}.em-flag-cu{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1e8-1f1fa.png")}.em-flag-cu.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1e8-1f1fa.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1e8-1f1fa.svg"),none}.em-flag-cv{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1e8-1f1fb.png")}.em-flag-cv.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1e8-1f1fb.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1e8-1f1fb.svg"),none}.em-flag-cw{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1e8-1f1fc.png")}.em-flag-cw.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1e8-1f1fc.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1e8-1f1fc.svg"),none}.em-flag-cx{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1e8-1f1fd.png")}.em-flag-cx.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1e8-1f1fd.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1e8-1f1fd.svg"),none}.em-flag-cy{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1e8-1f1fe.png")}.em-flag-cy.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1e8-1f1fe.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1e8-1f1fe.svg"),none}.em-flag-cz{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1e8-1f1ff.png")}.em-flag-cz.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1e8-1f1ff.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1e8-1f1ff.svg"),none}.em-flag-dg{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1e9-1f1ec.png")}.em-flag-dg.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1e9-1f1ec.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1e9-1f1ec.svg"),none}.em-flag-dj{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1e9-1f1ef.png")}.em-flag-dj.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1e9-1f1ef.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1e9-1f1ef.svg"),none}.em-flag-dk{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1e9-1f1f0.png")}.em-flag-dk.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1e9-1f1f0.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1e9-1f1f0.svg"),none}.em-flag-dm{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1e9-1f1f2.png")}.em-flag-dm.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1e9-1f1f2.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1e9-1f1f2.svg"),none}.em-flag-do{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1e9-1f1f4.png")}.em-flag-do.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1e9-1f1f4.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1e9-1f1f4.svg"),none}.em-flag-dz{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1e9-1f1ff.png")}.em-flag-dz.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1e9-1f1ff.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1e9-1f1ff.svg"),none}.em-flag-ea{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1ea-1f1e6.png")}.em-flag-ea.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1ea-1f1e6.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1ea-1f1e6.svg"),none}.em-flag-ec{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1ea-1f1e8.png")}.em-flag-ec.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1ea-1f1e8.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1ea-1f1e8.svg"),none}.em-flag-ee{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1ea-1f1ea.png")}.em-flag-ee.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1ea-1f1ea.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1ea-1f1ea.svg"),none}.em-flag-eg{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1ea-1f1ec.png")}.em-flag-eg.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1ea-1f1ec.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1ea-1f1ec.svg"),none}.em-flag-eh{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1ea-1f1ed.png")}.em-flag-eh.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1ea-1f1ed.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1ea-1f1ed.svg"),none}.em-flag-england{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f3f4-e0067-e0062-e0065-e006e-e0067-e007f.png")}.em-flag-england.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f3f4-e0067-e0062-e0065-e006e-e0067-e007f.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f3f4-e0067-e0062-e0065-e006e-e0067-e007f.svg"),none}.em-flag-er{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1ea-1f1f7.png")}.em-flag-er.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1ea-1f1f7.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1ea-1f1f7.svg"),none}.em-flag-et{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1ea-1f1f9.png")}.em-flag-et.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1ea-1f1f9.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1ea-1f1f9.svg"),none}.em-flag-eu{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1ea-1f1fa.png")}.em-flag-eu.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1ea-1f1fa.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1ea-1f1fa.svg"),none}.em-flag-fi{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1eb-1f1ee.png")}.em-flag-fi.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1eb-1f1ee.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1eb-1f1ee.svg"),none}.em-flag-fj{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1eb-1f1ef.png")}.em-flag-fj.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1eb-1f1ef.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1eb-1f1ef.svg"),none}.em-flag-fk{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1eb-1f1f0.png")}.em-flag-fk.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1eb-1f1f0.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1eb-1f1f0.svg"),none}.em-flag-fm{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1eb-1f1f2.png")}.em-flag-fm.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1eb-1f1f2.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1eb-1f1f2.svg"),none}.em-flag-fo{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1eb-1f1f4.png")}.em-flag-fo.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1eb-1f1f4.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1eb-1f1f4.svg"),none}.em-flag-ga{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1ec-1f1e6.png")}.em-flag-ga.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1ec-1f1e6.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1ec-1f1e6.svg"),none}.em-flag-gd{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1ec-1f1e9.png")}.em-flag-gd.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1ec-1f1e9.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1ec-1f1e9.svg"),none}.em-flag-ge{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1ec-1f1ea.png")}.em-flag-ge.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1ec-1f1ea.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1ec-1f1ea.svg"),none}.em-flag-gf{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1ec-1f1eb.png")}.em-flag-gf.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1ec-1f1eb.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1ec-1f1eb.svg"),none}.em-flag-gg{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1ec-1f1ec.png")}.em-flag-gg.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1ec-1f1ec.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1ec-1f1ec.svg"),none}.em-flag-gh{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1ec-1f1ed.png")}.em-flag-gh.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1ec-1f1ed.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1ec-1f1ed.svg"),none}.em-flag-gi{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1ec-1f1ee.png")}.em-flag-gi.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1ec-1f1ee.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1ec-1f1ee.svg"),none}.em-flag-gl{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1ec-1f1f1.png")}.em-flag-gl.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1ec-1f1f1.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1ec-1f1f1.svg"),none}.em-flag-gm{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1ec-1f1f2.png")}.em-flag-gm.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1ec-1f1f2.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1ec-1f1f2.svg"),none}.em-flag-gn{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1ec-1f1f3.png")}.em-flag-gn.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1ec-1f1f3.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1ec-1f1f3.svg"),none}.em-flag-gp{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1ec-1f1f5.png")}.em-flag-gp.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1ec-1f1f5.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1ec-1f1f5.svg"),none}.em-flag-gq{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1ec-1f1f6.png")}.em-flag-gq.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1ec-1f1f6.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1ec-1f1f6.svg"),none}.em-flag-gr{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1ec-1f1f7.png")}.em-flag-gr.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1ec-1f1f7.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1ec-1f1f7.svg"),none}.em-flag-gs{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1ec-1f1f8.png")}.em-flag-gs.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1ec-1f1f8.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1ec-1f1f8.svg"),none}.em-flag-gt{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1ec-1f1f9.png")}.em-flag-gt.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1ec-1f1f9.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1ec-1f1f9.svg"),none}.em-flag-gu{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1ec-1f1fa.png")}.em-flag-gu.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1ec-1f1fa.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1ec-1f1fa.svg"),none}.em-flag-gw{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1ec-1f1fc.png")}.em-flag-gw.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1ec-1f1fc.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1ec-1f1fc.svg"),none}.em-flag-gy{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1ec-1f1fe.png")}.em-flag-gy.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1ec-1f1fe.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1ec-1f1fe.svg"),none}.em-flag-hk{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1ed-1f1f0.png")}.em-flag-hk.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1ed-1f1f0.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1ed-1f1f0.svg"),none}.em-flag-hm{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1ed-1f1f2.png")}.em-flag-hm.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1ed-1f1f2.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1ed-1f1f2.svg"),none}.em-flag-hn{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1ed-1f1f3.png")}.em-flag-hn.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1ed-1f1f3.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1ed-1f1f3.svg"),none}.em-flag-hr{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1ed-1f1f7.png")}.em-flag-hr.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1ed-1f1f7.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1ed-1f1f7.svg"),none}.em-flag-ht{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1ed-1f1f9.png")}.em-flag-ht.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1ed-1f1f9.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1ed-1f1f9.svg"),none}.em-flag-hu{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1ed-1f1fa.png")}.em-flag-hu.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1ed-1f1fa.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1ed-1f1fa.svg"),none}.em-flag-ic{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1ee-1f1e8.png")}.em-flag-ic.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1ee-1f1e8.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1ee-1f1e8.svg"),none}.em-flag-id{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1ee-1f1e9.png")}.em-flag-id.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1ee-1f1e9.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1ee-1f1e9.svg"),none}.em-flag-ie{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1ee-1f1ea.png")}.em-flag-ie.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1ee-1f1ea.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1ee-1f1ea.svg"),none}.em-flag-il{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1ee-1f1f1.png")}.em-flag-il.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1ee-1f1f1.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1ee-1f1f1.svg"),none}.em-flag-im{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1ee-1f1f2.png")}.em-flag-im.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1ee-1f1f2.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1ee-1f1f2.svg"),none}.em-flag-in{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1ee-1f1f3.png")}.em-flag-in.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1ee-1f1f3.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1ee-1f1f3.svg"),none}.em-flag-io{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1ee-1f1f4.png")}.em-flag-io.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1ee-1f1f4.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1ee-1f1f4.svg"),none}.em-flag-iq{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1ee-1f1f6.png")}.em-flag-iq.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1ee-1f1f6.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1ee-1f1f6.svg"),none}.em-flag-ir{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1ee-1f1f7.png")}.em-flag-ir.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1ee-1f1f7.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1ee-1f1f7.svg"),none}.em-flag-is{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1ee-1f1f8.png")}.em-flag-is.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1ee-1f1f8.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1ee-1f1f8.svg"),none}.em-flag-je{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1ef-1f1ea.png")}.em-flag-je.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1ef-1f1ea.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1ef-1f1ea.svg"),none}.em-flag-jm{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1ef-1f1f2.png")}.em-flag-jm.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1ef-1f1f2.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1ef-1f1f2.svg"),none}.em-flag-jo{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1ef-1f1f4.png")}.em-flag-jo.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1ef-1f1f4.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1ef-1f1f4.svg"),none}.em-flag-ke{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1f0-1f1ea.png")}.em-flag-ke.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1f0-1f1ea.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1f0-1f1ea.svg"),none}.em-flag-kg{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1f0-1f1ec.png")}.em-flag-kg.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1f0-1f1ec.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1f0-1f1ec.svg"),none}.em-flag-kh{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1f0-1f1ed.png")}.em-flag-kh.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1f0-1f1ed.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1f0-1f1ed.svg"),none}.em-flag-ki{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1f0-1f1ee.png")}.em-flag-ki.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1f0-1f1ee.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1f0-1f1ee.svg"),none}.em-flag-km{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1f0-1f1f2.png")}.em-flag-km.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1f0-1f1f2.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1f0-1f1f2.svg"),none}.em-flag-kn{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1f0-1f1f3.png")}.em-flag-kn.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1f0-1f1f3.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1f0-1f1f3.svg"),none}.em-flag-kp{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1f0-1f1f5.png")}.em-flag-kp.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1f0-1f1f5.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1f0-1f1f5.svg"),none}.em-flag-kw{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1f0-1f1fc.png")}.em-flag-kw.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1f0-1f1fc.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1f0-1f1fc.svg"),none}.em-flag-ky{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1f0-1f1fe.png")}.em-flag-ky.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1f0-1f1fe.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1f0-1f1fe.svg"),none}.em-flag-kz{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1f0-1f1ff.png")}.em-flag-kz.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1f0-1f1ff.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1f0-1f1ff.svg"),none}.em-flag-la{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1f1-1f1e6.png")}.em-flag-la.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1f1-1f1e6.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1f1-1f1e6.svg"),none}.em-flag-lb{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1f1-1f1e7.png")}.em-flag-lb.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1f1-1f1e7.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1f1-1f1e7.svg"),none}.em-flag-lc{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1f1-1f1e8.png")}.em-flag-lc.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1f1-1f1e8.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1f1-1f1e8.svg"),none}.em-flag-li{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1f1-1f1ee.png")}.em-flag-li.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1f1-1f1ee.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1f1-1f1ee.svg"),none}.em-flag-lk{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1f1-1f1f0.png")}.em-flag-lk.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1f1-1f1f0.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1f1-1f1f0.svg"),none}.em-flag-lr{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1f1-1f1f7.png")}.em-flag-lr.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1f1-1f1f7.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1f1-1f1f7.svg"),none}.em-flag-ls{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1f1-1f1f8.png")}.em-flag-ls.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1f1-1f1f8.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1f1-1f1f8.svg"),none}.em-flag-lt{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1f1-1f1f9.png")}.em-flag-lt.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1f1-1f1f9.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1f1-1f1f9.svg"),none}.em-flag-lu{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1f1-1f1fa.png")}.em-flag-lu.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1f1-1f1fa.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1f1-1f1fa.svg"),none}.em-flag-lv{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1f1-1f1fb.png")}.em-flag-lv.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1f1-1f1fb.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1f1-1f1fb.svg"),none}.em-flag-ly{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1f1-1f1fe.png")}.em-flag-ly.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1f1-1f1fe.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1f1-1f1fe.svg"),none}.em-flag-ma{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1f2-1f1e6.png")}.em-flag-ma.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1f2-1f1e6.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1f2-1f1e6.svg"),none}.em-flag-mc{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1f2-1f1e8.png")}.em-flag-mc.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1f2-1f1e8.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1f2-1f1e8.svg"),none}.em-flag-md{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1f2-1f1e9.png")}.em-flag-md.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1f2-1f1e9.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1f2-1f1e9.svg"),none}.em-flag-me{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1f2-1f1ea.png")}.em-flag-me.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1f2-1f1ea.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1f2-1f1ea.svg"),none}.em-flag-mf{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1f2-1f1eb.png")}.em-flag-mf.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1f2-1f1eb.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1f2-1f1eb.svg"),none}.em-flag-mg{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1f2-1f1ec.png")}.em-flag-mg.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1f2-1f1ec.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1f2-1f1ec.svg"),none}.em-flag-mh{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1f2-1f1ed.png")}.em-flag-mh.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1f2-1f1ed.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1f2-1f1ed.svg"),none}.em-flag-mk{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1f2-1f1f0.png")}.em-flag-mk.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1f2-1f1f0.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1f2-1f1f0.svg"),none}.em-flag-ml{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1f2-1f1f1.png")}.em-flag-ml.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1f2-1f1f1.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1f2-1f1f1.svg"),none}.em-flag-mm{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1f2-1f1f2.png")}.em-flag-mm.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1f2-1f1f2.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1f2-1f1f2.svg"),none}.em-flag-mn{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1f2-1f1f3.png")}.em-flag-mn.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1f2-1f1f3.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1f2-1f1f3.svg"),none}.em-flag-mo{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1f2-1f1f4.png")}.em-flag-mo.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1f2-1f1f4.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1f2-1f1f4.svg"),none}.em-flag-mp{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1f2-1f1f5.png")}.em-flag-mp.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1f2-1f1f5.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1f2-1f1f5.svg"),none}.em-flag-mq{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1f2-1f1f6.png")}.em-flag-mq.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1f2-1f1f6.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1f2-1f1f6.svg"),none}.em-flag-mr{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1f2-1f1f7.png")}.em-flag-mr.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1f2-1f1f7.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1f2-1f1f7.svg"),none}.em-flag-ms{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1f2-1f1f8.png")}.em-flag-ms.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1f2-1f1f8.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1f2-1f1f8.svg"),none}.em-flag-mt{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1f2-1f1f9.png")}.em-flag-mt.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1f2-1f1f9.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1f2-1f1f9.svg"),none}.em-flag-mu{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1f2-1f1fa.png")}.em-flag-mu.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1f2-1f1fa.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1f2-1f1fa.svg"),none}.em-flag-mv{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1f2-1f1fb.png")}.em-flag-mv.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1f2-1f1fb.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1f2-1f1fb.svg"),none}.em-flag-mw{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1f2-1f1fc.png")}.em-flag-mw.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1f2-1f1fc.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1f2-1f1fc.svg"),none}.em-flag-mx{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1f2-1f1fd.png")}.em-flag-mx.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1f2-1f1fd.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1f2-1f1fd.svg"),none}.em-flag-my{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1f2-1f1fe.png")}.em-flag-my.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1f2-1f1fe.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1f2-1f1fe.svg"),none}.em-flag-mz{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1f2-1f1ff.png")}.em-flag-mz.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1f2-1f1ff.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1f2-1f1ff.svg"),none}.em-flag-na{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1f3-1f1e6.png")}.em-flag-na.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1f3-1f1e6.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1f3-1f1e6.svg"),none}.em-flag-nc{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1f3-1f1e8.png")}.em-flag-nc.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1f3-1f1e8.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1f3-1f1e8.svg"),none}.em-flag-ne{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1f3-1f1ea.png")}.em-flag-ne.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1f3-1f1ea.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1f3-1f1ea.svg"),none}.em-flag-nf{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1f3-1f1eb.png")}.em-flag-nf.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1f3-1f1eb.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1f3-1f1eb.svg"),none}.em-flag-ng{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1f3-1f1ec.png")}.em-flag-ng.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1f3-1f1ec.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1f3-1f1ec.svg"),none}.em-flag-ni{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1f3-1f1ee.png")}.em-flag-ni.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1f3-1f1ee.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1f3-1f1ee.svg"),none}.em-flag-nl{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1f3-1f1f1.png")}.em-flag-nl.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1f3-1f1f1.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1f3-1f1f1.svg"),none}.em-flag-no{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1f3-1f1f4.png")}.em-flag-no.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1f3-1f1f4.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1f3-1f1f4.svg"),none}.em-flag-np{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1f3-1f1f5.png")}.em-flag-np.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1f3-1f1f5.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1f3-1f1f5.svg"),none}.em-flag-nr{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1f3-1f1f7.png")}.em-flag-nr.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1f3-1f1f7.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1f3-1f1f7.svg"),none}.em-flag-nu{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1f3-1f1fa.png")}.em-flag-nu.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1f3-1f1fa.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1f3-1f1fa.svg"),none}.em-flag-nz{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1f3-1f1ff.png")}.em-flag-nz.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1f3-1f1ff.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1f3-1f1ff.svg"),none}.em-flag-om{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1f4-1f1f2.png")}.em-flag-om.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1f4-1f1f2.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1f4-1f1f2.svg"),none}.em-flag-pa{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1f5-1f1e6.png")}.em-flag-pa.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1f5-1f1e6.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1f5-1f1e6.svg"),none}.em-flag-pe{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1f5-1f1ea.png")}.em-flag-pe.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1f5-1f1ea.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1f5-1f1ea.svg"),none}.em-flag-pf{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1f5-1f1eb.png")}.em-flag-pf.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1f5-1f1eb.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1f5-1f1eb.svg"),none}.em-flag-pg{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1f5-1f1ec.png")}.em-flag-pg.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1f5-1f1ec.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1f5-1f1ec.svg"),none}.em-flag-ph{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1f5-1f1ed.png")}.em-flag-ph.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1f5-1f1ed.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1f5-1f1ed.svg"),none}.em-flag-pk{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1f5-1f1f0.png")}.em-flag-pk.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1f5-1f1f0.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1f5-1f1f0.svg"),none}.em-flag-pl{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1f5-1f1f1.png")}.em-flag-pl.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1f5-1f1f1.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1f5-1f1f1.svg"),none}.em-flag-pm{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1f5-1f1f2.png")}.em-flag-pm.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1f5-1f1f2.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1f5-1f1f2.svg"),none}.em-flag-pn{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1f5-1f1f3.png")}.em-flag-pn.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1f5-1f1f3.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1f5-1f1f3.svg"),none}.em-flag-pr{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1f5-1f1f7.png")}.em-flag-pr.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1f5-1f1f7.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1f5-1f1f7.svg"),none}.em-flag-ps{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1f5-1f1f8.png")}.em-flag-ps.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1f5-1f1f8.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1f5-1f1f8.svg"),none}.em-flag-pt{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1f5-1f1f9.png")}.em-flag-pt.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1f5-1f1f9.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1f5-1f1f9.svg"),none}.em-flag-pw{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1f5-1f1fc.png")}.em-flag-pw.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1f5-1f1fc.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1f5-1f1fc.svg"),none}.em-flag-py{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1f5-1f1fe.png")}.em-flag-py.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1f5-1f1fe.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1f5-1f1fe.svg"),none}.em-flag-qa{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1f6-1f1e6.png")}.em-flag-qa.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1f6-1f1e6.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1f6-1f1e6.svg"),none}.em-flag-re{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1f7-1f1ea.png")}.em-flag-re.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1f7-1f1ea.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1f7-1f1ea.svg"),none}.em-flag-ro{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1f7-1f1f4.png")}.em-flag-ro.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1f7-1f1f4.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1f7-1f1f4.svg"),none}.em-flag-rs{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1f7-1f1f8.png")}.em-flag-rs.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1f7-1f1f8.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1f7-1f1f8.svg"),none}.em-flag-rw{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1f7-1f1fc.png")}.em-flag-rw.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1f7-1f1fc.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1f7-1f1fc.svg"),none}.em-flag-sa{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1f8-1f1e6.png")}.em-flag-sa.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1f8-1f1e6.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1f8-1f1e6.svg"),none}.em-flag-sb{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1f8-1f1e7.png")}.em-flag-sb.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1f8-1f1e7.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1f8-1f1e7.svg"),none}.em-flag-sc{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1f8-1f1e8.png")}.em-flag-sc.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1f8-1f1e8.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1f8-1f1e8.svg"),none}.em-flag-scotland{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f3f4-e0067-e0062-e0073-e0063-e0074-e007f.png")}.em-flag-scotland.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f3f4-e0067-e0062-e0073-e0063-e0074-e007f.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f3f4-e0067-e0062-e0073-e0063-e0074-e007f.svg"),none}.em-flag-sd{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1f8-1f1e9.png")}.em-flag-sd.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1f8-1f1e9.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1f8-1f1e9.svg"),none}.em-flag-se{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1f8-1f1ea.png")}.em-flag-se.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1f8-1f1ea.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1f8-1f1ea.svg"),none}.em-flag-sg{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1f8-1f1ec.png")}.em-flag-sg.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1f8-1f1ec.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1f8-1f1ec.svg"),none}.em-flag-sh{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1f8-1f1ed.png")}.em-flag-sh.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1f8-1f1ed.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1f8-1f1ed.svg"),none}.em-flag-si{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1f8-1f1ee.png")}.em-flag-si.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1f8-1f1ee.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1f8-1f1ee.svg"),none}.em-flag-sj{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1f8-1f1ef.png")}.em-flag-sj.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1f8-1f1ef.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1f8-1f1ef.svg"),none}.em-flag-sk{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1f8-1f1f0.png")}.em-flag-sk.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1f8-1f1f0.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1f8-1f1f0.svg"),none}.em-flag-sl{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1f8-1f1f1.png")}.em-flag-sl.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1f8-1f1f1.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1f8-1f1f1.svg"),none}.em-flag-sm{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1f8-1f1f2.png")}.em-flag-sm.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1f8-1f1f2.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1f8-1f1f2.svg"),none}.em-flag-sn{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1f8-1f1f3.png")}.em-flag-sn.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1f8-1f1f3.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1f8-1f1f3.svg"),none}.em-flag-so{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1f8-1f1f4.png")}.em-flag-so.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1f8-1f1f4.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1f8-1f1f4.svg"),none}.em-flag-sr{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1f8-1f1f7.png")}.em-flag-sr.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1f8-1f1f7.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1f8-1f1f7.svg"),none}.em-flag-ss{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1f8-1f1f8.png")}.em-flag-ss.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1f8-1f1f8.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1f8-1f1f8.svg"),none}.em-flag-st{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1f8-1f1f9.png")}.em-flag-st.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1f8-1f1f9.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1f8-1f1f9.svg"),none}.em-flag-sv{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1f8-1f1fb.png")}.em-flag-sv.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1f8-1f1fb.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1f8-1f1fb.svg"),none}.em-flag-sx{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1f8-1f1fd.png")}.em-flag-sx.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1f8-1f1fd.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1f8-1f1fd.svg"),none}.em-flag-sy{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1f8-1f1fe.png")}.em-flag-sy.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1f8-1f1fe.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1f8-1f1fe.svg"),none}.em-flag-sz{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1f8-1f1ff.png")}.em-flag-sz.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1f8-1f1ff.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1f8-1f1ff.svg"),none}.em-flag-ta{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1f9-1f1e6.png")}.em-flag-ta.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1f9-1f1e6.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1f9-1f1e6.svg"),none}.em-flag-tc{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1f9-1f1e8.png")}.em-flag-tc.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1f9-1f1e8.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1f9-1f1e8.svg"),none}.em-flag-td{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1f9-1f1e9.png")}.em-flag-td.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1f9-1f1e9.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1f9-1f1e9.svg"),none}.em-flag-tf{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1f9-1f1eb.png")}.em-flag-tf.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1f9-1f1eb.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1f9-1f1eb.svg"),none}.em-flag-tg{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1f9-1f1ec.png")}.em-flag-tg.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1f9-1f1ec.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1f9-1f1ec.svg"),none}.em-flag-th{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1f9-1f1ed.png")}.em-flag-th.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1f9-1f1ed.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1f9-1f1ed.svg"),none}.em-flag-tj{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1f9-1f1ef.png")}.em-flag-tj.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1f9-1f1ef.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1f9-1f1ef.svg"),none}.em-flag-tk{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1f9-1f1f0.png")}.em-flag-tk.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1f9-1f1f0.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1f9-1f1f0.svg"),none}.em-flag-tl{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1f9-1f1f1.png")}.em-flag-tl.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1f9-1f1f1.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1f9-1f1f1.svg"),none}.em-flag-tm{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1f9-1f1f2.png")}.em-flag-tm.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1f9-1f1f2.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1f9-1f1f2.svg"),none}.em-flag-tn{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1f9-1f1f3.png")}.em-flag-tn.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1f9-1f1f3.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1f9-1f1f3.svg"),none}.em-flag-to{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1f9-1f1f4.png")}.em-flag-to.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1f9-1f1f4.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1f9-1f1f4.svg"),none}.em-flag-tr{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1f9-1f1f7.png")}.em-flag-tr.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1f9-1f1f7.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1f9-1f1f7.svg"),none}.em-flag-tt{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1f9-1f1f9.png")}.em-flag-tt.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1f9-1f1f9.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1f9-1f1f9.svg"),none}.em-flag-tv{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1f9-1f1fb.png")}.em-flag-tv.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1f9-1f1fb.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1f9-1f1fb.svg"),none}.em-flag-tw{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1f9-1f1fc.png")}.em-flag-tw.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1f9-1f1fc.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1f9-1f1fc.svg"),none}.em-flag-tz{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1f9-1f1ff.png")}.em-flag-tz.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1f9-1f1ff.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1f9-1f1ff.svg"),none}.em-flag-ua{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1fa-1f1e6.png")}.em-flag-ua.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1fa-1f1e6.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1fa-1f1e6.svg"),none}.em-flag-ug{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1fa-1f1ec.png")}.em-flag-ug.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1fa-1f1ec.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1fa-1f1ec.svg"),none}.em-flag-um{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1fa-1f1f2.png")}.em-flag-um.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1fa-1f1f2.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1fa-1f1f2.svg"),none}.em-flag-un{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1fa-1f1f3.png")}.em-flag-un.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1fa-1f1f3.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1fa-1f1f3.svg"),none}.em-flag-uy{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1fa-1f1fe.png")}.em-flag-uy.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1fa-1f1fe.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1fa-1f1fe.svg"),none}.em-flag-uz{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1fa-1f1ff.png")}.em-flag-uz.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1fa-1f1ff.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1fa-1f1ff.svg"),none}.em-flag-va{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1fb-1f1e6.png")}.em-flag-va.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1fb-1f1e6.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1fb-1f1e6.svg"),none}.em-flag-vc{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1fb-1f1e8.png")}.em-flag-vc.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1fb-1f1e8.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1fb-1f1e8.svg"),none}.em-flag-ve{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1fb-1f1ea.png")}.em-flag-ve.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1fb-1f1ea.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1fb-1f1ea.svg"),none}.em-flag-vg{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1fb-1f1ec.png")}.em-flag-vg.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1fb-1f1ec.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1fb-1f1ec.svg"),none}.em-flag-vi{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1fb-1f1ee.png")}.em-flag-vi.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1fb-1f1ee.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1fb-1f1ee.svg"),none}.em-flag-vn{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1fb-1f1f3.png")}.em-flag-vn.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1fb-1f1f3.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1fb-1f1f3.svg"),none}.em-flag-vu{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1fb-1f1fa.png")}.em-flag-vu.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1fb-1f1fa.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1fb-1f1fa.svg"),none}.em-flag-wales{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f3f4-e0067-e0062-e0077-e006c-e0073-e007f.png")}.em-flag-wales.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f3f4-e0067-e0062-e0077-e006c-e0073-e007f.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f3f4-e0067-e0062-e0077-e006c-e0073-e007f.svg"),none}.em-flag-wf{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1fc-1f1eb.png")}.em-flag-wf.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1fc-1f1eb.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1fc-1f1eb.svg"),none}.em-flag-ws{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1fc-1f1f8.png")}.em-flag-ws.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1fc-1f1f8.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1fc-1f1f8.svg"),none}.em-flag-xk{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1fd-1f1f0.png")}.em-flag-xk.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1fd-1f1f0.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1fd-1f1f0.svg"),none}.em-flag-ye{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1fe-1f1ea.png")}.em-flag-ye.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1fe-1f1ea.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1fe-1f1ea.svg"),none}.em-flag-yt{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1fe-1f1f9.png")}.em-flag-yt.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1fe-1f1f9.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1fe-1f1f9.svg"),none}.em-flag-za{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1ff-1f1e6.png")}.em-flag-za.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1ff-1f1e6.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1ff-1f1e6.svg"),none}.em-flag-zm{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1ff-1f1f2.png")}.em-flag-zm.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1ff-1f1f2.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1ff-1f1f2.svg"),none}.em-flag-zw{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1ff-1f1fc.png")}.em-flag-zw.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1ff-1f1fc.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1ff-1f1fc.svg"),none}.em-flags{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f38f.png")}.em-flags.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f38f.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f38f.svg"),none}.em-flashlight{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f526.png")}.em-flashlight.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f526.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f526.svg"),none}.em-fleur_de_lis{background-image:url("https://twemoji.maxcdn.com/2/72x72/269c.png")}.em-fleur_de_lis.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/269c.png");background-image:url("https://twemoji.maxcdn.com/2/svg/269c.svg"),none}.em-floppy_disk{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f4be.png")}.em-floppy_disk.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f4be.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f4be.svg"),none}.em-flower_playing_cards{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f3b4.png")}.em-flower_playing_cards.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f3b4.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f3b4.svg"),none}.em-flushed{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f633.png")}.em-flushed.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f633.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f633.svg"),none}.em-flying_saucer{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f6f8.png")}.em-flying_saucer.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f6f8.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f6f8.svg"),none}.em-fog{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f32b.png")}.em-fog.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f32b.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f32b.svg"),none}.em-foggy{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f301.png")}.em-foggy.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f301.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f301.svg"),none}.em-football{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f3c8.png")}.em-football.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f3c8.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f3c8.svg"),none}.em-footprints{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f463.png")}.em-footprints.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f463.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f463.svg"),none}.em-fork_and_knife{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f374.png")}.em-fork_and_knife.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f374.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f374.svg"),none}.em-fortune_cookie{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f960.png")}.em-fortune_cookie.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f960.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f960.svg"),none}.em-fountain{background-image:url("https://twemoji.maxcdn.com/2/72x72/26f2.png")}.em-fountain.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/26f2.png");background-image:url("https://twemoji.maxcdn.com/2/svg/26f2.svg"),none}.em-four{background-image:url("https://twemoji.maxcdn.com/2/72x72/34-20e3.png")}.em-four.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/34-20e3.png");background-image:url("https://twemoji.maxcdn.com/2/svg/34-20e3.svg"),none}.em-four_leaf_clover{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f340.png")}.em-four_leaf_clover.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f340.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f340.svg"),none}.em-fox_face{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f98a.png")}.em-fox_face.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f98a.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f98a.svg"),none}.em-fr{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1eb-1f1f7.png")}.em-fr.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1eb-1f1f7.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1eb-1f1f7.svg"),none}.em-frame_with_picture{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f5bc.png")}.em-frame_with_picture.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f5bc.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f5bc.svg"),none}.em-free{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f193.png")}.em-free.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f193.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f193.svg"),none}.em-fried_egg{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f373.png")}.em-fried_egg.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f373.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f373.svg"),none}.em-fried_shrimp{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f364.png")}.em-fried_shrimp.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f364.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f364.svg"),none}.em-fries{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f35f.png")}.em-fries.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f35f.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f35f.svg"),none}.em-frog{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f438.png")}.em-frog.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f438.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f438.svg"),none}.em-frowning{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f626.png")}.em-frowning.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f626.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f626.svg"),none}.em-fuelpump{background-image:url("https://twemoji.maxcdn.com/2/72x72/26fd.png")}.em-fuelpump.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/26fd.png");background-image:url("https://twemoji.maxcdn.com/2/svg/26fd.svg"),none}.em-full_moon{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f315.png")}.em-full_moon.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f315.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f315.svg"),none}.em-full_moon_with_face{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f31d.png")}.em-full_moon_with_face.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f31d.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f31d.svg"),none}.em-funeral_urn{background-image:url("https://twemoji.maxcdn.com/2/72x72/26b1.png")}.em-funeral_urn.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/26b1.png");background-image:url("https://twemoji.maxcdn.com/2/svg/26b1.svg"),none}.em-game_die{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f3b2.png")}.em-game_die.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f3b2.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f3b2.svg"),none}.em-gb{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1ec-1f1e7.png")}.em-gb.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1ec-1f1e7.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1ec-1f1e7.svg"),none}.em-gear{background-image:url("https://twemoji.maxcdn.com/2/72x72/2699.png")}.em-gear.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/2699.png");background-image:url("https://twemoji.maxcdn.com/2/svg/2699.svg"),none}.em-gem{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f48e.png")}.em-gem.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f48e.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f48e.svg"),none}.em-gemini{background-image:url("https://twemoji.maxcdn.com/2/72x72/264a.png")}.em-gemini.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/264a.png");background-image:url("https://twemoji.maxcdn.com/2/svg/264a.svg"),none}.em-genie{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f9de.png")}.em-genie.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f9de.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f9de.svg"),none}.em-ghost{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f47b.png")}.em-ghost.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f47b.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f47b.svg"),none}.em-gift{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f381.png")}.em-gift.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f381.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f381.svg"),none}.em-gift_heart{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f49d.png")}.em-gift_heart.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f49d.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f49d.svg"),none}.em-giraffe_face{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f992.png")}.em-giraffe_face.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f992.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f992.svg"),none}.em-girl{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f467.png")}.em-girl.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f467.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f467.svg"),none}.em-glass_of_milk{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f95b.png")}.em-glass_of_milk.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f95b.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f95b.svg"),none}.em-globe_with_meridians{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f310.png")}.em-globe_with_meridians.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f310.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f310.svg"),none}.em-gloves{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f9e4.png")}.em-gloves.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f9e4.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f9e4.svg"),none}.em-goal_net{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f945.png")}.em-goal_net.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f945.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f945.svg"),none}.em-goat{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f410.png")}.em-goat.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f410.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f410.svg"),none}.em-golf{background-image:url("https://twemoji.maxcdn.com/2/72x72/26f3.png")}.em-golf.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/26f3.png");background-image:url("https://twemoji.maxcdn.com/2/svg/26f3.svg"),none}.em-golfer{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f3cc.png")}.em-golfer.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f3cc.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f3cc.svg"),none}.em-gorilla{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f98d.png")}.em-gorilla.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f98d.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f98d.svg"),none}.em-grapes{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f347.png")}.em-grapes.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f347.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f347.svg"),none}.em-green_apple{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f34f.png")}.em-green_apple.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f34f.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f34f.svg"),none}.em-green_book{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f4d7.png")}.em-green_book.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f4d7.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f4d7.svg"),none}.em-green_heart{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f49a.png")}.em-green_heart.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f49a.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f49a.svg"),none}.em-green_salad{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f957.png")}.em-green_salad.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f957.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f957.svg"),none}.em-grey_exclamation{background-image:url("https://twemoji.maxcdn.com/2/72x72/2755.png")}.em-grey_exclamation.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/2755.png");background-image:url("https://twemoji.maxcdn.com/2/svg/2755.svg"),none}.em-grey_question{background-image:url("https://twemoji.maxcdn.com/2/72x72/2754.png")}.em-grey_question.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/2754.png");background-image:url("https://twemoji.maxcdn.com/2/svg/2754.svg"),none}.em-grimacing{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f62c.png")}.em-grimacing.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f62c.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f62c.svg"),none}.em-grin{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f601.png")}.em-grin.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f601.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f601.svg"),none}.em-grinning{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f600.png")}.em-grinning.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f600.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f600.svg"),none}.em-grinning_face_with_one_large_and_one_small_eye{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f92a.png")}.em-grinning_face_with_one_large_and_one_small_eye.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f92a.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f92a.svg"),none}.em-grinning_face_with_star_eyes{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f929.png")}.em-grinning_face_with_star_eyes.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f929.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f929.svg"),none}.em-guardsman{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f482.png")}.em-guardsman.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f482.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f482.svg"),none}.em-guitar{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f3b8.png")}.em-guitar.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f3b8.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f3b8.svg"),none}.em-gun{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f52b.png")}.em-gun.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f52b.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f52b.svg"),none}.em-haircut{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f487.png")}.em-haircut.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f487.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f487.svg"),none}.em-hamburger{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f354.png")}.em-hamburger.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f354.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f354.svg"),none}.em-hammer{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f528.png")}.em-hammer.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f528.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f528.svg"),none}.em-hammer_and_pick{background-image:url("https://twemoji.maxcdn.com/2/72x72/2692.png")}.em-hammer_and_pick.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/2692.png");background-image:url("https://twemoji.maxcdn.com/2/svg/2692.svg"),none}.em-hammer_and_wrench{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f6e0.png")}.em-hammer_and_wrench.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f6e0.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f6e0.svg"),none}.em-hamster{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f439.png")}.em-hamster.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f439.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f439.svg"),none}.em-hand{background-image:url("https://twemoji.maxcdn.com/2/72x72/270b.png")}.em-hand.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/270b.png");background-image:url("https://twemoji.maxcdn.com/2/svg/270b.svg"),none}.em-hand_with_index_and_middle_fingers_crossed{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f91e.png")}.em-hand_with_index_and_middle_fingers_crossed.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f91e.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f91e.svg"),none}.em-handbag{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f45c.png")}.em-handbag.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f45c.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f45c.svg"),none}.em-handball{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f93e.png")}.em-handball.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f93e.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f93e.svg"),none}.em-handshake{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f91d.png")}.em-handshake.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f91d.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f91d.svg"),none}.em-hankey{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f4a9.png")}.em-hankey.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f4a9.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f4a9.svg"),none}.em-hash{background-image:url("https://twemoji.maxcdn.com/2/72x72/23-20e3.png")}.em-hash.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/23-20e3.png");background-image:url("https://twemoji.maxcdn.com/2/svg/23-20e3.svg"),none}.em-hatched_chick{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f425.png")}.em-hatched_chick.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f425.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f425.svg"),none}.em-hatching_chick{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f423.png")}.em-hatching_chick.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f423.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f423.svg"),none}.em-headphones{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f3a7.png")}.em-headphones.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f3a7.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f3a7.svg"),none}.em-hear_no_evil{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f649.png")}.em-hear_no_evil.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f649.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f649.svg"),none}.em-heart{background-image:url("https://twemoji.maxcdn.com/2/72x72/2764.png")}.em-heart.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/2764.png");background-image:url("https://twemoji.maxcdn.com/2/svg/2764.svg"),none}.em-heart_decoration{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f49f.png")}.em-heart_decoration.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f49f.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f49f.svg"),none}.em-heart_eyes{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f60d.png")}.em-heart_eyes.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f60d.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f60d.svg"),none}.em-heart_eyes_cat{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f63b.png")}.em-heart_eyes_cat.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f63b.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f63b.svg"),none}.em-heartbeat{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f493.png")}.em-heartbeat.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f493.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f493.svg"),none}.em-heartpulse{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f497.png")}.em-heartpulse.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f497.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f497.svg"),none}.em-hearts{background-image:url("https://twemoji.maxcdn.com/2/72x72/2665.png")}.em-hearts.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/2665.png");background-image:url("https://twemoji.maxcdn.com/2/svg/2665.svg"),none}.em-heavy_check_mark{background-image:url("https://twemoji.maxcdn.com/2/72x72/2714.png")}.em-heavy_check_mark.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/2714.png");background-image:url("https://twemoji.maxcdn.com/2/svg/2714.svg"),none}.em-heavy_division_sign{background-image:url("https://twemoji.maxcdn.com/2/72x72/2797.png")}.em-heavy_division_sign.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/2797.png");background-image:url("https://twemoji.maxcdn.com/2/svg/2797.svg"),none}.em-heavy_dollar_sign{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f4b2.png")}.em-heavy_dollar_sign.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f4b2.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f4b2.svg"),none}.em-heavy_heart_exclamation_mark_ornament{background-image:url("https://twemoji.maxcdn.com/2/72x72/2763.png")}.em-heavy_heart_exclamation_mark_ornament.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/2763.png");background-image:url("https://twemoji.maxcdn.com/2/svg/2763.svg"),none}.em-heavy_minus_sign{background-image:url("https://twemoji.maxcdn.com/2/72x72/2796.png")}.em-heavy_minus_sign.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/2796.png");background-image:url("https://twemoji.maxcdn.com/2/svg/2796.svg"),none}.em-heavy_multiplication_x{background-image:url("https://twemoji.maxcdn.com/2/72x72/2716.png")}.em-heavy_multiplication_x.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/2716.png");background-image:url("https://twemoji.maxcdn.com/2/svg/2716.svg"),none}.em-heavy_plus_sign{background-image:url("https://twemoji.maxcdn.com/2/72x72/2795.png")}.em-heavy_plus_sign.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/2795.png");background-image:url("https://twemoji.maxcdn.com/2/svg/2795.svg"),none}.em-hedgehog{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f994.png")}.em-hedgehog.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f994.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f994.svg"),none}.em-helicopter{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f681.png")}.em-helicopter.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f681.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f681.svg"),none}.em-helmet_with_white_cross{background-image:url("https://twemoji.maxcdn.com/2/72x72/26d1.png")}.em-helmet_with_white_cross.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/26d1.png");background-image:url("https://twemoji.maxcdn.com/2/svg/26d1.svg"),none}.em-herb{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f33f.png")}.em-herb.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f33f.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f33f.svg"),none}.em-hibiscus{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f33a.png")}.em-hibiscus.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f33a.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f33a.svg"),none}.em-high_brightness{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f506.png")}.em-high_brightness.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f506.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f506.svg"),none}.em-high_heel{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f460.png")}.em-high_heel.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f460.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f460.svg"),none}.em-hocho{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f52a.png")}.em-hocho.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f52a.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f52a.svg"),none}.em-hole{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f573.png")}.em-hole.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f573.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f573.svg"),none}.em-honey_pot{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f36f.png")}.em-honey_pot.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f36f.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f36f.svg"),none}.em-horse{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f434.png")}.em-horse.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f434.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f434.svg"),none}.em-horse_racing{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f3c7.png")}.em-horse_racing.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f3c7.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f3c7.svg"),none}.em-hospital{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f3e5.png")}.em-hospital.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f3e5.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f3e5.svg"),none}.em-hot_pepper{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f336.png")}.em-hot_pepper.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f336.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f336.svg"),none}.em-hotdog{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f32d.png")}.em-hotdog.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f32d.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f32d.svg"),none}.em-hotel{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f3e8.png")}.em-hotel.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f3e8.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f3e8.svg"),none}.em-hotsprings{background-image:url("https://twemoji.maxcdn.com/2/72x72/2668.png")}.em-hotsprings.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/2668.png");background-image:url("https://twemoji.maxcdn.com/2/svg/2668.svg"),none}.em-hourglass{background-image:url("https://twemoji.maxcdn.com/2/72x72/231b.png")}.em-hourglass.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/231b.png");background-image:url("https://twemoji.maxcdn.com/2/svg/231b.svg"),none}.em-hourglass_flowing_sand{background-image:url("https://twemoji.maxcdn.com/2/72x72/23f3.png")}.em-hourglass_flowing_sand.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/23f3.png");background-image:url("https://twemoji.maxcdn.com/2/svg/23f3.svg"),none}.em-house{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f3e0.png")}.em-house.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f3e0.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f3e0.svg"),none}.em-house_buildings{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f3d8.png")}.em-house_buildings.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f3d8.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f3d8.svg"),none}.em-house_with_garden{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f3e1.png")}.em-house_with_garden.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f3e1.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f3e1.svg"),none}.em-hugging_face{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f917.png")}.em-hugging_face.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f917.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f917.svg"),none}.em-hushed{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f62f.png")}.em-hushed.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f62f.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f62f.svg"),none}.em-i_love_you_hand_sign{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f91f.png")}.em-i_love_you_hand_sign.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f91f.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f91f.svg"),none}.em-ice_cream{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f368.png")}.em-ice_cream.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f368.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f368.svg"),none}.em-ice_hockey_stick_and_puck{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f3d2.png")}.em-ice_hockey_stick_and_puck.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f3d2.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f3d2.svg"),none}.em-ice_skate{background-image:url("https://twemoji.maxcdn.com/2/72x72/26f8.png")}.em-ice_skate.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/26f8.png");background-image:url("https://twemoji.maxcdn.com/2/svg/26f8.svg"),none}.em-icecream{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f366.png")}.em-icecream.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f366.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f366.svg"),none}.em-id{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f194.png")}.em-id.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f194.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f194.svg"),none}.em-ideograph_advantage{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f250.png")}.em-ideograph_advantage.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f250.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f250.svg"),none}.em-imp{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f47f.png")}.em-imp.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f47f.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f47f.svg"),none}.em-inbox_tray{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f4e5.png")}.em-inbox_tray.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f4e5.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f4e5.svg"),none}.em-incoming_envelope{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f4e8.png")}.em-incoming_envelope.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f4e8.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f4e8.svg"),none}.em-information_desk_person{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f481.png")}.em-information_desk_person.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f481.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f481.svg"),none}.em-information_source{background-image:url("https://twemoji.maxcdn.com/2/72x72/2139.png")}.em-information_source.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/2139.png");background-image:url("https://twemoji.maxcdn.com/2/svg/2139.svg"),none}.em-innocent{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f607.png")}.em-innocent.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f607.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f607.svg"),none}.em-interrobang{background-image:url("https://twemoji.maxcdn.com/2/72x72/2049.png")}.em-interrobang.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/2049.png");background-image:url("https://twemoji.maxcdn.com/2/svg/2049.svg"),none}.em-iphone{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f4f1.png")}.em-iphone.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f4f1.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f4f1.svg"),none}.em-it{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1ee-1f1f9.png")}.em-it.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1ee-1f1f9.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1ee-1f1f9.svg"),none}.em-izakaya_lantern{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f3ee.png")}.em-izakaya_lantern.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f3ee.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f3ee.svg"),none}.em-jack_o_lantern{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f383.png")}.em-jack_o_lantern.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f383.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f383.svg"),none}.em-japan{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f5fe.png")}.em-japan.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f5fe.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f5fe.svg"),none}.em-japanese_castle{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f3ef.png")}.em-japanese_castle.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f3ef.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f3ef.svg"),none}.em-japanese_goblin{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f47a.png")}.em-japanese_goblin.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f47a.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f47a.svg"),none}.em-japanese_ogre{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f479.png")}.em-japanese_ogre.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f479.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f479.svg"),none}.em-jeans{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f456.png")}.em-jeans.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f456.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f456.svg"),none}.em-joy{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f602.png")}.em-joy.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f602.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f602.svg"),none}.em-joy_cat{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f639.png")}.em-joy_cat.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f639.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f639.svg"),none}.em-joystick{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f579.png")}.em-joystick.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f579.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f579.svg"),none}.em-jp{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1ef-1f1f5.png")}.em-jp.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1ef-1f1f5.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1ef-1f1f5.svg"),none}.em-juggling{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f939.png")}.em-juggling.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f939.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f939.svg"),none}.em-kaaba{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f54b.png")}.em-kaaba.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f54b.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f54b.svg"),none}.em-key{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f511.png")}.em-key.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f511.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f511.svg"),none}.em-keyboard{background-image:url("https://twemoji.maxcdn.com/2/72x72/2328.png")}.em-keyboard.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/2328.png");background-image:url("https://twemoji.maxcdn.com/2/svg/2328.svg"),none}.em-keycap_star{background-image:url("https://twemoji.maxcdn.com/2/72x72/2a-20e3.png")}.em-keycap_star.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/2a-20e3.png");background-image:url("https://twemoji.maxcdn.com/2/svg/2a-20e3.svg"),none}.em-keycap_ten{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f51f.png")}.em-keycap_ten.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f51f.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f51f.svg"),none}.em-kimono{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f458.png")}.em-kimono.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f458.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f458.svg"),none}.em-kiss{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f48b.png")}.em-kiss.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f48b.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f48b.svg"),none}.em-kissing{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f617.png")}.em-kissing.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f617.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f617.svg"),none}.em-kissing_cat{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f63d.png")}.em-kissing_cat.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f63d.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f63d.svg"),none}.em-kissing_closed_eyes{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f61a.png")}.em-kissing_closed_eyes.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f61a.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f61a.svg"),none}.em-kissing_heart{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f618.png")}.em-kissing_heart.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f618.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f618.svg"),none}.em-kissing_smiling_eyes{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f619.png")}.em-kissing_smiling_eyes.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f619.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f619.svg"),none}.em-kiwifruit{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f95d.png")}.em-kiwifruit.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f95d.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f95d.svg"),none}.em-knife_fork_plate{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f37d.png")}.em-knife_fork_plate.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f37d.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f37d.svg"),none}.em-koala{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f428.png")}.em-koala.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f428.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f428.svg"),none}.em-koko{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f201.png")}.em-koko.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f201.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f201.svg"),none}.em-kr{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1f0-1f1f7.png")}.em-kr.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1f0-1f1f7.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1f0-1f1f7.svg"),none}.em-label{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f3f7.png")}.em-label.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f3f7.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f3f7.svg"),none}.em-large_blue_circle{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f535.png")}.em-large_blue_circle.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f535.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f535.svg"),none}.em-large_blue_diamond{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f537.png")}.em-large_blue_diamond.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f537.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f537.svg"),none}.em-large_orange_diamond{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f536.png")}.em-large_orange_diamond.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f536.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f536.svg"),none}.em-last_quarter_moon{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f317.png")}.em-last_quarter_moon.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f317.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f317.svg"),none}.em-last_quarter_moon_with_face{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f31c.png")}.em-last_quarter_moon_with_face.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f31c.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f31c.svg"),none}.em-latin_cross{background-image:url("https://twemoji.maxcdn.com/2/72x72/271d.png")}.em-latin_cross.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/271d.png");background-image:url("https://twemoji.maxcdn.com/2/svg/271d.svg"),none}.em-laughing{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f606.png")}.em-laughing.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f606.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f606.svg"),none}.em-leaves{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f343.png")}.em-leaves.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f343.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f343.svg"),none}.em-ledger{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f4d2.png")}.em-ledger.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f4d2.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f4d2.svg"),none}.em-left-facing_fist{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f91b.png")}.em-left-facing_fist.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f91b.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f91b.svg"),none}.em-left_luggage{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f6c5.png")}.em-left_luggage.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f6c5.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f6c5.svg"),none}.em-left_right_arrow{background-image:url("https://twemoji.maxcdn.com/2/72x72/2194.png")}.em-left_right_arrow.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/2194.png");background-image:url("https://twemoji.maxcdn.com/2/svg/2194.svg"),none}.em-left_speech_bubble{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f5e8.png")}.em-left_speech_bubble.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f5e8.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f5e8.svg"),none}.em-leftwards_arrow_with_hook{background-image:url("https://twemoji.maxcdn.com/2/72x72/21a9.png")}.em-leftwards_arrow_with_hook.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/21a9.png");background-image:url("https://twemoji.maxcdn.com/2/svg/21a9.svg"),none}.em-lemon{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f34b.png")}.em-lemon.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f34b.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f34b.svg"),none}.em-leo{background-image:url("https://twemoji.maxcdn.com/2/72x72/264c.png")}.em-leo.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/264c.png");background-image:url("https://twemoji.maxcdn.com/2/svg/264c.svg"),none}.em-leopard{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f406.png")}.em-leopard.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f406.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f406.svg"),none}.em-level_slider{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f39a.png")}.em-level_slider.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f39a.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f39a.svg"),none}.em-libra{background-image:url("https://twemoji.maxcdn.com/2/72x72/264e.png")}.em-libra.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/264e.png");background-image:url("https://twemoji.maxcdn.com/2/svg/264e.svg"),none}.em-light_rail{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f688.png")}.em-light_rail.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f688.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f688.svg"),none}.em-lightning{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f329.png")}.em-lightning.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f329.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f329.svg"),none}.em-link{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f517.png")}.em-link.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f517.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f517.svg"),none}.em-linked_paperclips{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f587.png")}.em-linked_paperclips.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f587.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f587.svg"),none}.em-lion_face{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f981.png")}.em-lion_face.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f981.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f981.svg"),none}.em-lips{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f444.png")}.em-lips.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f444.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f444.svg"),none}.em-lipstick{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f484.png")}.em-lipstick.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f484.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f484.svg"),none}.em-lizard{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f98e.png")}.em-lizard.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f98e.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f98e.svg"),none}.em-lock{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f512.png")}.em-lock.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f512.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f512.svg"),none}.em-lock_with_ink_pen{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f50f.png")}.em-lock_with_ink_pen.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f50f.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f50f.svg"),none}.em-lollipop{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f36d.png")}.em-lollipop.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f36d.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f36d.svg"),none}.em-loop{background-image:url("https://twemoji.maxcdn.com/2/72x72/27bf.png")}.em-loop.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/27bf.png");background-image:url("https://twemoji.maxcdn.com/2/svg/27bf.svg"),none}.em-loud_sound{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f50a.png")}.em-loud_sound.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f50a.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f50a.svg"),none}.em-loudspeaker{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f4e2.png")}.em-loudspeaker.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f4e2.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f4e2.svg"),none}.em-love_hotel{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f3e9.png")}.em-love_hotel.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f3e9.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f3e9.svg"),none}.em-love_letter{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f48c.png")}.em-love_letter.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f48c.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f48c.svg"),none}.em-low_brightness{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f505.png")}.em-low_brightness.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f505.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f505.svg"),none}.em-lower_left_ballpoint_pen{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f58a.png")}.em-lower_left_ballpoint_pen.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f58a.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f58a.svg"),none}.em-lower_left_crayon{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f58d.png")}.em-lower_left_crayon.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f58d.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f58d.svg"),none}.em-lower_left_fountain_pen{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f58b.png")}.em-lower_left_fountain_pen.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f58b.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f58b.svg"),none}.em-lower_left_paintbrush{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f58c.png")}.em-lower_left_paintbrush.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f58c.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f58c.svg"),none}.em-lying_face{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f925.png")}.em-lying_face.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f925.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f925.svg"),none}.em-m{background-image:url("https://twemoji.maxcdn.com/2/72x72/24c2.png")}.em-m.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/24c2.png");background-image:url("https://twemoji.maxcdn.com/2/svg/24c2.svg"),none}.em-mag{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f50d.png")}.em-mag.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f50d.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f50d.svg"),none}.em-mag_right{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f50e.png")}.em-mag_right.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f50e.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f50e.svg"),none}.em-mage{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f9d9.png")}.em-mage.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f9d9.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f9d9.svg"),none}.em-mahjong{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f004.png")}.em-mahjong.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f004.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f004.svg"),none}.em-mailbox{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f4eb.png")}.em-mailbox.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f4eb.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f4eb.svg"),none}.em-mailbox_closed{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f4ea.png")}.em-mailbox_closed.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f4ea.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f4ea.svg"),none}.em-mailbox_with_mail{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f4ec.png")}.em-mailbox_with_mail.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f4ec.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f4ec.svg"),none}.em-mailbox_with_no_mail{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f4ed.png")}.em-mailbox_with_no_mail.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f4ed.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f4ed.svg"),none}.em-male-artist{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f468-200d-1f3a8.png")}.em-male-artist.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f468-200d-1f3a8.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f468-200d-1f3a8.svg"),none}.em-male-astronaut{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f468-200d-1f680.png")}.em-male-astronaut.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f468-200d-1f680.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f468-200d-1f680.svg"),none}.em-male-construction-worker{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f477-200d-2642-fe0f.png")}.em-male-construction-worker.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f477-200d-2642-fe0f.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f477-200d-2642-fe0f.svg"),none}.em-male-cook{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f468-200d-1f373.png")}.em-male-cook.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f468-200d-1f373.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f468-200d-1f373.svg"),none}.em-male-detective{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f575-fe0f-200d-2642-fe0f.png")}.em-male-detective.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f575-fe0f-200d-2642-fe0f.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f575-fe0f-200d-2642-fe0f.svg"),none}.em-male-doctor{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f468-200d-2695-fe0f.png")}.em-male-doctor.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f468-200d-2695-fe0f.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f468-200d-2695-fe0f.svg"),none}.em-male-factory-worker{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f468-200d-1f3ed.png")}.em-male-factory-worker.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f468-200d-1f3ed.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f468-200d-1f3ed.svg"),none}.em-male-farmer{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f468-200d-1f33e.png")}.em-male-farmer.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f468-200d-1f33e.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f468-200d-1f33e.svg"),none}.em-male-firefighter{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f468-200d-1f692.png")}.em-male-firefighter.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f468-200d-1f692.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f468-200d-1f692.svg"),none}.em-male-guard{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f482-200d-2642-fe0f.png")}.em-male-guard.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f482-200d-2642-fe0f.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f482-200d-2642-fe0f.svg"),none}.em-male-judge{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f468-200d-2696-fe0f.png")}.em-male-judge.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f468-200d-2696-fe0f.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f468-200d-2696-fe0f.svg"),none}.em-male-mechanic{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f468-200d-1f527.png")}.em-male-mechanic.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f468-200d-1f527.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f468-200d-1f527.svg"),none}.em-male-office-worker{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f468-200d-1f4bc.png")}.em-male-office-worker.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f468-200d-1f4bc.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f468-200d-1f4bc.svg"),none}.em-male-pilot{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f468-200d-2708-fe0f.png")}.em-male-pilot.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f468-200d-2708-fe0f.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f468-200d-2708-fe0f.svg"),none}.em-male-police-officer{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f46e-200d-2642-fe0f.png")}.em-male-police-officer.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f46e-200d-2642-fe0f.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f46e-200d-2642-fe0f.svg"),none}.em-male-scientist{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f468-200d-1f52c.png")}.em-male-scientist.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f468-200d-1f52c.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f468-200d-1f52c.svg"),none}.em-male-singer{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f468-200d-1f3a4.png")}.em-male-singer.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f468-200d-1f3a4.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f468-200d-1f3a4.svg"),none}.em-male-student{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f468-200d-1f393.png")}.em-male-student.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f468-200d-1f393.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f468-200d-1f393.svg"),none}.em-male-teacher{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f468-200d-1f3eb.png")}.em-male-teacher.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f468-200d-1f3eb.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f468-200d-1f3eb.svg"),none}.em-male-technologist{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f468-200d-1f4bb.png")}.em-male-technologist.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f468-200d-1f4bb.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f468-200d-1f4bb.svg"),none}.em-male_elf{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f9dd-200d-2642-fe0f.png")}.em-male_elf.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f9dd-200d-2642-fe0f.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f9dd-200d-2642-fe0f.svg"),none}.em-male_fairy{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f9da-200d-2642-fe0f.png")}.em-male_fairy.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f9da-200d-2642-fe0f.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f9da-200d-2642-fe0f.svg"),none}.em-male_genie{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f9de-200d-2642-fe0f.png")}.em-male_genie.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f9de-200d-2642-fe0f.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f9de-200d-2642-fe0f.svg"),none}.em-male_mage{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f9d9-200d-2642-fe0f.png")}.em-male_mage.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f9d9-200d-2642-fe0f.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f9d9-200d-2642-fe0f.svg"),none}.em-male_sign{background-image:url("https://twemoji.maxcdn.com/2/72x72/2642.png")}.em-male_sign.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/2642.png");background-image:url("https://twemoji.maxcdn.com/2/svg/2642.svg"),none}.em-male_vampire{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f9db-200d-2642-fe0f.png")}.em-male_vampire.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f9db-200d-2642-fe0f.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f9db-200d-2642-fe0f.svg"),none}.em-male_zombie{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f9df-200d-2642-fe0f.png")}.em-male_zombie.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f9df-200d-2642-fe0f.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f9df-200d-2642-fe0f.svg"),none}.em-man{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f468.png")}.em-man.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f468.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f468.svg"),none}.em-man-biking{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f6b4-200d-2642-fe0f.png")}.em-man-biking.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f6b4-200d-2642-fe0f.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f6b4-200d-2642-fe0f.svg"),none}.em-man-bouncing-ball{background-image:url("https://twemoji.maxcdn.com/2/72x72/26f9-fe0f-200d-2642-fe0f.png")}.em-man-bouncing-ball.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/26f9-fe0f-200d-2642-fe0f.png");background-image:url("https://twemoji.maxcdn.com/2/svg/26f9-fe0f-200d-2642-fe0f.svg"),none}.em-man-bowing{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f647-200d-2642-fe0f.png")}.em-man-bowing.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f647-200d-2642-fe0f.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f647-200d-2642-fe0f.svg"),none}.em-man-boy{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f468-200d-1f466.png")}.em-man-boy.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f468-200d-1f466.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f468-200d-1f466.svg"),none}.em-man-boy-boy{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f468-200d-1f466-200d-1f466.png")}.em-man-boy-boy.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f468-200d-1f466-200d-1f466.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f468-200d-1f466-200d-1f466.svg"),none}.em-man-cartwheeling{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f938-200d-2642-fe0f.png")}.em-man-cartwheeling.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f938-200d-2642-fe0f.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f938-200d-2642-fe0f.svg"),none}.em-man-facepalming{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f926-200d-2642-fe0f.png")}.em-man-facepalming.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f926-200d-2642-fe0f.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f926-200d-2642-fe0f.svg"),none}.em-man-frowning{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f64d-200d-2642-fe0f.png")}.em-man-frowning.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f64d-200d-2642-fe0f.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f64d-200d-2642-fe0f.svg"),none}.em-man-gesturing-no{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f645-200d-2642-fe0f.png")}.em-man-gesturing-no.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f645-200d-2642-fe0f.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f645-200d-2642-fe0f.svg"),none}.em-man-gesturing-ok{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f646-200d-2642-fe0f.png")}.em-man-gesturing-ok.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f646-200d-2642-fe0f.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f646-200d-2642-fe0f.svg"),none}.em-man-getting-haircut{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f487-200d-2642-fe0f.png")}.em-man-getting-haircut.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f487-200d-2642-fe0f.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f487-200d-2642-fe0f.svg"),none}.em-man-getting-massage{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f486-200d-2642-fe0f.png")}.em-man-getting-massage.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f486-200d-2642-fe0f.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f486-200d-2642-fe0f.svg"),none}.em-man-girl{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f468-200d-1f467.png")}.em-man-girl.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f468-200d-1f467.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f468-200d-1f467.svg"),none}.em-man-girl-boy{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f468-200d-1f467-200d-1f466.png")}.em-man-girl-boy.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f468-200d-1f467-200d-1f466.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f468-200d-1f467-200d-1f466.svg"),none}.em-man-girl-girl{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f468-200d-1f467-200d-1f467.png")}.em-man-girl-girl.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f468-200d-1f467-200d-1f467.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f468-200d-1f467-200d-1f467.svg"),none}.em-man-golfing{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f3cc-fe0f-200d-2642-fe0f.png")}.em-man-golfing.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f3cc-fe0f-200d-2642-fe0f.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f3cc-fe0f-200d-2642-fe0f.svg"),none}.em-man-heart-man{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f468-200d-2764-fe0f-200d-1f468.png")}.em-man-heart-man.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f468-200d-2764-fe0f-200d-1f468.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f468-200d-2764-fe0f-200d-1f468.svg"),none}.em-man-juggling{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f939-200d-2642-fe0f.png")}.em-man-juggling.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f939-200d-2642-fe0f.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f939-200d-2642-fe0f.svg"),none}.em-man-kiss-man{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f468-200d-2764-fe0f-200d-1f48b-200d-1f468.png")}.em-man-kiss-man.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f468-200d-2764-fe0f-200d-1f48b-200d-1f468.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f468-200d-2764-fe0f-200d-1f48b-200d-1f468.svg"),none}.em-man-lifting-weights{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f3cb-fe0f-200d-2642-fe0f.png")}.em-man-lifting-weights.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f3cb-fe0f-200d-2642-fe0f.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f3cb-fe0f-200d-2642-fe0f.svg"),none}.em-man-man-boy{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f468-200d-1f468-200d-1f466.png")}.em-man-man-boy.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f468-200d-1f468-200d-1f466.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f468-200d-1f468-200d-1f466.svg"),none}.em-man-man-boy-boy{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f468-200d-1f468-200d-1f466-200d-1f466.png")}.em-man-man-boy-boy.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f468-200d-1f468-200d-1f466-200d-1f466.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f468-200d-1f468-200d-1f466-200d-1f466.svg"),none}.em-man-man-girl{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f468-200d-1f468-200d-1f467.png")}.em-man-man-girl.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f468-200d-1f468-200d-1f467.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f468-200d-1f468-200d-1f467.svg"),none}.em-man-man-girl-boy{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f468-200d-1f468-200d-1f467-200d-1f466.png")}.em-man-man-girl-boy.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f468-200d-1f468-200d-1f467-200d-1f466.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f468-200d-1f468-200d-1f467-200d-1f466.svg"),none}.em-man-man-girl-girl{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f468-200d-1f468-200d-1f467-200d-1f467.png")}.em-man-man-girl-girl.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f468-200d-1f468-200d-1f467-200d-1f467.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f468-200d-1f468-200d-1f467-200d-1f467.svg"),none}.em-man-mountain-biking{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f6b5-200d-2642-fe0f.png")}.em-man-mountain-biking.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f6b5-200d-2642-fe0f.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f6b5-200d-2642-fe0f.svg"),none}.em-man-playing-handball{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f93e-200d-2642-fe0f.png")}.em-man-playing-handball.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f93e-200d-2642-fe0f.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f93e-200d-2642-fe0f.svg"),none}.em-man-playing-water-polo{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f93d-200d-2642-fe0f.png")}.em-man-playing-water-polo.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f93d-200d-2642-fe0f.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f93d-200d-2642-fe0f.svg"),none}.em-man-pouting{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f64e-200d-2642-fe0f.png")}.em-man-pouting.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f64e-200d-2642-fe0f.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f64e-200d-2642-fe0f.svg"),none}.em-man-raising-hand{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f64b-200d-2642-fe0f.png")}.em-man-raising-hand.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f64b-200d-2642-fe0f.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f64b-200d-2642-fe0f.svg"),none}.em-man-rowing-boat{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f6a3-200d-2642-fe0f.png")}.em-man-rowing-boat.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f6a3-200d-2642-fe0f.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f6a3-200d-2642-fe0f.svg"),none}.em-man-running{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f3c3-200d-2642-fe0f.png")}.em-man-running.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f3c3-200d-2642-fe0f.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f3c3-200d-2642-fe0f.svg"),none}.em-man-shrugging{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f937-200d-2642-fe0f.png")}.em-man-shrugging.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f937-200d-2642-fe0f.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f937-200d-2642-fe0f.svg"),none}.em-man-surfing{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f3c4-200d-2642-fe0f.png")}.em-man-surfing.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f3c4-200d-2642-fe0f.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f3c4-200d-2642-fe0f.svg"),none}.em-man-swimming{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f3ca-200d-2642-fe0f.png")}.em-man-swimming.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f3ca-200d-2642-fe0f.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f3ca-200d-2642-fe0f.svg"),none}.em-man-tipping-hand{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f481-200d-2642-fe0f.png")}.em-man-tipping-hand.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f481-200d-2642-fe0f.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f481-200d-2642-fe0f.svg"),none}.em-man-walking{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f6b6-200d-2642-fe0f.png")}.em-man-walking.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f6b6-200d-2642-fe0f.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f6b6-200d-2642-fe0f.svg"),none}.em-man-wearing-turban{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f473-200d-2642-fe0f.png")}.em-man-wearing-turban.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f473-200d-2642-fe0f.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f473-200d-2642-fe0f.svg"),none}.em-man-with-bunny-ears-partying{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f46f-200d-2642-fe0f.png")}.em-man-with-bunny-ears-partying.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f46f-200d-2642-fe0f.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f46f-200d-2642-fe0f.svg"),none}.em-man-woman-boy{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f468-200d-1f469-200d-1f466.png")}.em-man-woman-boy.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f468-200d-1f469-200d-1f466.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f468-200d-1f469-200d-1f466.svg"),none}.em-man-woman-boy-boy{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f468-200d-1f469-200d-1f466-200d-1f466.png")}.em-man-woman-boy-boy.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f468-200d-1f469-200d-1f466-200d-1f466.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f468-200d-1f469-200d-1f466-200d-1f466.svg"),none}.em-man-woman-girl{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f468-200d-1f469-200d-1f467.png")}.em-man-woman-girl.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f468-200d-1f469-200d-1f467.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f468-200d-1f469-200d-1f467.svg"),none}.em-man-woman-girl-boy{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f468-200d-1f469-200d-1f467-200d-1f466.png")}.em-man-woman-girl-boy.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f468-200d-1f469-200d-1f467-200d-1f466.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f468-200d-1f469-200d-1f467-200d-1f466.svg"),none}.em-man-woman-girl-girl{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f468-200d-1f469-200d-1f467-200d-1f467.png")}.em-man-woman-girl-girl.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f468-200d-1f469-200d-1f467-200d-1f467.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f468-200d-1f469-200d-1f467-200d-1f467.svg"),none}.em-man-wrestling{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f93c-200d-2642-fe0f.png")}.em-man-wrestling.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f93c-200d-2642-fe0f.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f93c-200d-2642-fe0f.svg"),none}.em-man_climbing{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f9d7-200d-2642-fe0f.png")}.em-man_climbing.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f9d7-200d-2642-fe0f.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f9d7-200d-2642-fe0f.svg"),none}.em-man_dancing{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f57a.png")}.em-man_dancing.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f57a.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f57a.svg"),none}.em-man_in_business_suit_levitating{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f574.png")}.em-man_in_business_suit_levitating.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f574.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f574.svg"),none}.em-man_in_lotus_position{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f9d8-200d-2642-fe0f.png")}.em-man_in_lotus_position.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f9d8-200d-2642-fe0f.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f9d8-200d-2642-fe0f.svg"),none}.em-man_in_steamy_room{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f9d6-200d-2642-fe0f.png")}.em-man_in_steamy_room.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f9d6-200d-2642-fe0f.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f9d6-200d-2642-fe0f.svg"),none}.em-man_in_tuxedo{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f935.png")}.em-man_in_tuxedo.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f935.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f935.svg"),none}.em-man_with_gua_pi_mao{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f472.png")}.em-man_with_gua_pi_mao.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f472.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f472.svg"),none}.em-man_with_turban{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f473.png")}.em-man_with_turban.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f473.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f473.svg"),none}.em-mans_shoe{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f45e.png")}.em-mans_shoe.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f45e.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f45e.svg"),none}.em-mantelpiece_clock{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f570.png")}.em-mantelpiece_clock.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f570.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f570.svg"),none}.em-maple_leaf{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f341.png")}.em-maple_leaf.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f341.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f341.svg"),none}.em-martial_arts_uniform{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f94b.png")}.em-martial_arts_uniform.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f94b.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f94b.svg"),none}.em-mask{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f637.png")}.em-mask.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f637.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f637.svg"),none}.em-massage{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f486.png")}.em-massage.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f486.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f486.svg"),none}.em-meat_on_bone{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f356.png")}.em-meat_on_bone.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f356.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f356.svg"),none}.em-medal{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f396.png")}.em-medal.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f396.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f396.svg"),none}.em-mega{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f4e3.png")}.em-mega.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f4e3.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f4e3.svg"),none}.em-melon{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f348.png")}.em-melon.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f348.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f348.svg"),none}.em-memo{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f4dd.png")}.em-memo.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f4dd.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f4dd.svg"),none}.em-menorah_with_nine_branches{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f54e.png")}.em-menorah_with_nine_branches.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f54e.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f54e.svg"),none}.em-mens{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f6b9.png")}.em-mens.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f6b9.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f6b9.svg"),none}.em-mermaid{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f9dc-200d-2640-fe0f.png")}.em-mermaid.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f9dc-200d-2640-fe0f.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f9dc-200d-2640-fe0f.svg"),none}.em-merman{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f9dc-200d-2642-fe0f.png")}.em-merman.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f9dc-200d-2642-fe0f.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f9dc-200d-2642-fe0f.svg"),none}.em-merperson{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f9dc.png")}.em-merperson.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f9dc.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f9dc.svg"),none}.em-metro{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f687.png")}.em-metro.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f687.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f687.svg"),none}.em-microphone{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f3a4.png")}.em-microphone.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f3a4.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f3a4.svg"),none}.em-microscope{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f52c.png")}.em-microscope.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f52c.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f52c.svg"),none}.em-middle_finger{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f595.png")}.em-middle_finger.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f595.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f595.svg"),none}.em-milky_way{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f30c.png")}.em-milky_way.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f30c.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f30c.svg"),none}.em-minibus{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f690.png")}.em-minibus.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f690.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f690.svg"),none}.em-minidisc{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f4bd.png")}.em-minidisc.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f4bd.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f4bd.svg"),none}.em-mobile_phone_off{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f4f4.png")}.em-mobile_phone_off.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f4f4.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f4f4.svg"),none}.em-money_mouth_face{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f911.png")}.em-money_mouth_face.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f911.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f911.svg"),none}.em-money_with_wings{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f4b8.png")}.em-money_with_wings.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f4b8.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f4b8.svg"),none}.em-moneybag{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f4b0.png")}.em-moneybag.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f4b0.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f4b0.svg"),none}.em-monkey{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f412.png")}.em-monkey.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f412.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f412.svg"),none}.em-monkey_face{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f435.png")}.em-monkey_face.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f435.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f435.svg"),none}.em-monorail{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f69d.png")}.em-monorail.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f69d.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f69d.svg"),none}.em-moon{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f314.png")}.em-moon.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f314.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f314.svg"),none}.em-mortar_board{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f393.png")}.em-mortar_board.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f393.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f393.svg"),none}.em-mosque{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f54c.png")}.em-mosque.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f54c.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f54c.svg"),none}.em-mostly_sunny{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f324.png")}.em-mostly_sunny.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f324.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f324.svg"),none}.em-mother_christmas{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f936.png")}.em-mother_christmas.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f936.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f936.svg"),none}.em-motor_boat{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f6e5.png")}.em-motor_boat.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f6e5.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f6e5.svg"),none}.em-motor_scooter{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f6f5.png")}.em-motor_scooter.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f6f5.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f6f5.svg"),none}.em-motorway{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f6e3.png")}.em-motorway.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f6e3.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f6e3.svg"),none}.em-mount_fuji{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f5fb.png")}.em-mount_fuji.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f5fb.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f5fb.svg"),none}.em-mountain{background-image:url("https://twemoji.maxcdn.com/2/72x72/26f0.png")}.em-mountain.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/26f0.png");background-image:url("https://twemoji.maxcdn.com/2/svg/26f0.svg"),none}.em-mountain_bicyclist{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f6b5.png")}.em-mountain_bicyclist.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f6b5.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f6b5.svg"),none}.em-mountain_cableway{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f6a0.png")}.em-mountain_cableway.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f6a0.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f6a0.svg"),none}.em-mountain_railway{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f69e.png")}.em-mountain_railway.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f69e.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f69e.svg"),none}.em-mouse{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f42d.png")}.em-mouse.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f42d.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f42d.svg"),none}.em-mouse2{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f401.png")}.em-mouse2.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f401.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f401.svg"),none}.em-movie_camera{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f3a5.png")}.em-movie_camera.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f3a5.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f3a5.svg"),none}.em-moyai{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f5ff.png")}.em-moyai.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f5ff.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f5ff.svg"),none}.em-muscle{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f4aa.png")}.em-muscle.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f4aa.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f4aa.svg"),none}.em-mushroom{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f344.png")}.em-mushroom.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f344.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f344.svg"),none}.em-musical_keyboard{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f3b9.png")}.em-musical_keyboard.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f3b9.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f3b9.svg"),none}.em-musical_note{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f3b5.png")}.em-musical_note.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f3b5.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f3b5.svg"),none}.em-musical_score{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f3bc.png")}.em-musical_score.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f3bc.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f3bc.svg"),none}.em-mute{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f507.png")}.em-mute.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f507.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f507.svg"),none}.em-nail_care{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f485.png")}.em-nail_care.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f485.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f485.svg"),none}.em-name_badge{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f4db.png")}.em-name_badge.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f4db.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f4db.svg"),none}.em-national_park{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f3de.png")}.em-national_park.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f3de.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f3de.svg"),none}.em-nauseated_face{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f922.png")}.em-nauseated_face.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f922.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f922.svg"),none}.em-necktie{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f454.png")}.em-necktie.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f454.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f454.svg"),none}.em-negative_squared_cross_mark{background-image:url("https://twemoji.maxcdn.com/2/72x72/274e.png")}.em-negative_squared_cross_mark.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/274e.png");background-image:url("https://twemoji.maxcdn.com/2/svg/274e.svg"),none}.em-nerd_face{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f913.png")}.em-nerd_face.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f913.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f913.svg"),none}.em-neutral_face{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f610.png")}.em-neutral_face.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f610.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f610.svg"),none}.em-new{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f195.png")}.em-new.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f195.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f195.svg"),none}.em-new_moon{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f311.png")}.em-new_moon.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f311.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f311.svg"),none}.em-new_moon_with_face{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f31a.png")}.em-new_moon_with_face.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f31a.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f31a.svg"),none}.em-newspaper{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f4f0.png")}.em-newspaper.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f4f0.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f4f0.svg"),none}.em-ng{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f196.png")}.em-ng.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f196.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f196.svg"),none}.em-night_with_stars{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f303.png")}.em-night_with_stars.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f303.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f303.svg"),none}.em-nine{background-image:url("https://twemoji.maxcdn.com/2/72x72/39-20e3.png")}.em-nine.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/39-20e3.png");background-image:url("https://twemoji.maxcdn.com/2/svg/39-20e3.svg"),none}.em-no_bell{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f515.png")}.em-no_bell.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f515.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f515.svg"),none}.em-no_bicycles{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f6b3.png")}.em-no_bicycles.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f6b3.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f6b3.svg"),none}.em-no_entry{background-image:url("https://twemoji.maxcdn.com/2/72x72/26d4.png")}.em-no_entry.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/26d4.png");background-image:url("https://twemoji.maxcdn.com/2/svg/26d4.svg"),none}.em-no_entry_sign{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f6ab.png")}.em-no_entry_sign.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f6ab.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f6ab.svg"),none}.em-no_good{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f645.png")}.em-no_good.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f645.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f645.svg"),none}.em-no_mobile_phones{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f4f5.png")}.em-no_mobile_phones.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f4f5.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f4f5.svg"),none}.em-no_mouth{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f636.png")}.em-no_mouth.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f636.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f636.svg"),none}.em-no_pedestrians{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f6b7.png")}.em-no_pedestrians.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f6b7.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f6b7.svg"),none}.em-no_smoking{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f6ad.png")}.em-no_smoking.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f6ad.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f6ad.svg"),none}.em-non-potable_water{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f6b1.png")}.em-non-potable_water.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f6b1.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f6b1.svg"),none}.em-nose{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f443.png")}.em-nose.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f443.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f443.svg"),none}.em-notebook{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f4d3.png")}.em-notebook.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f4d3.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f4d3.svg"),none}.em-notebook_with_decorative_cover{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f4d4.png")}.em-notebook_with_decorative_cover.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f4d4.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f4d4.svg"),none}.em-notes{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f3b6.png")}.em-notes.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f3b6.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f3b6.svg"),none}.em-nut_and_bolt{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f529.png")}.em-nut_and_bolt.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f529.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f529.svg"),none}.em-o{background-image:url("https://twemoji.maxcdn.com/2/72x72/2b55.png")}.em-o.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/2b55.png");background-image:url("https://twemoji.maxcdn.com/2/svg/2b55.svg"),none}.em-o2{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f17e.png")}.em-o2.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f17e.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f17e.svg"),none}.em-ocean{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f30a.png")}.em-ocean.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f30a.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f30a.svg"),none}.em-octagonal_sign{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f6d1.png")}.em-octagonal_sign.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f6d1.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f6d1.svg"),none}.em-octopus{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f419.png")}.em-octopus.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f419.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f419.svg"),none}.em-oden{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f362.png")}.em-oden.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f362.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f362.svg"),none}.em-office{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f3e2.png")}.em-office.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f3e2.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f3e2.svg"),none}.em-oil_drum{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f6e2.png")}.em-oil_drum.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f6e2.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f6e2.svg"),none}.em-ok{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f197.png")}.em-ok.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f197.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f197.svg"),none}.em-ok_hand{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f44c.png")}.em-ok_hand.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f44c.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f44c.svg"),none}.em-ok_woman{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f646.png")}.em-ok_woman.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f646.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f646.svg"),none}.em-old_key{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f5dd.png")}.em-old_key.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f5dd.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f5dd.svg"),none}.em-older_adult{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f9d3.png")}.em-older_adult.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f9d3.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f9d3.svg"),none}.em-older_man{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f474.png")}.em-older_man.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f474.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f474.svg"),none}.em-older_woman{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f475.png")}.em-older_woman.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f475.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f475.svg"),none}.em-om_symbol{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f549.png")}.em-om_symbol.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f549.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f549.svg"),none}.em-on{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f51b.png")}.em-on.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f51b.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f51b.svg"),none}.em-oncoming_automobile{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f698.png")}.em-oncoming_automobile.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f698.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f698.svg"),none}.em-oncoming_bus{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f68d.png")}.em-oncoming_bus.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f68d.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f68d.svg"),none}.em-oncoming_police_car{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f694.png")}.em-oncoming_police_car.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f694.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f694.svg"),none}.em-oncoming_taxi{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f696.png")}.em-oncoming_taxi.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f696.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f696.svg"),none}.em-one{background-image:url("https://twemoji.maxcdn.com/2/72x72/31-20e3.png")}.em-one.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/31-20e3.png");background-image:url("https://twemoji.maxcdn.com/2/svg/31-20e3.svg"),none}.em-open_file_folder{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f4c2.png")}.em-open_file_folder.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f4c2.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f4c2.svg"),none}.em-open_hands{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f450.png")}.em-open_hands.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f450.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f450.svg"),none}.em-open_mouth{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f62e.png")}.em-open_mouth.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f62e.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f62e.svg"),none}.em-ophiuchus{background-image:url("https://twemoji.maxcdn.com/2/72x72/26ce.png")}.em-ophiuchus.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/26ce.png");background-image:url("https://twemoji.maxcdn.com/2/svg/26ce.svg"),none}.em-orange_book{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f4d9.png")}.em-orange_book.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f4d9.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f4d9.svg"),none}.em-orange_heart{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f9e1.png")}.em-orange_heart.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f9e1.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f9e1.svg"),none}.em-orthodox_cross{background-image:url("https://twemoji.maxcdn.com/2/72x72/2626.png")}.em-orthodox_cross.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/2626.png");background-image:url("https://twemoji.maxcdn.com/2/svg/2626.svg"),none}.em-outbox_tray{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f4e4.png")}.em-outbox_tray.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f4e4.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f4e4.svg"),none}.em-owl{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f989.png")}.em-owl.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f989.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f989.svg"),none}.em-ox{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f402.png")}.em-ox.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f402.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f402.svg"),none}.em-package{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f4e6.png")}.em-package.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f4e6.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f4e6.svg"),none}.em-page_facing_up{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f4c4.png")}.em-page_facing_up.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f4c4.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f4c4.svg"),none}.em-page_with_curl{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f4c3.png")}.em-page_with_curl.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f4c3.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f4c3.svg"),none}.em-pager{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f4df.png")}.em-pager.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f4df.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f4df.svg"),none}.em-palm_tree{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f334.png")}.em-palm_tree.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f334.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f334.svg"),none}.em-palms_up_together{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f932.png")}.em-palms_up_together.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f932.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f932.svg"),none}.em-pancakes{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f95e.png")}.em-pancakes.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f95e.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f95e.svg"),none}.em-panda_face{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f43c.png")}.em-panda_face.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f43c.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f43c.svg"),none}.em-paperclip{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f4ce.png")}.em-paperclip.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f4ce.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f4ce.svg"),none}.em-parking{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f17f.png")}.em-parking.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f17f.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f17f.svg"),none}.em-part_alternation_mark{background-image:url("https://twemoji.maxcdn.com/2/72x72/303d.png")}.em-part_alternation_mark.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/303d.png");background-image:url("https://twemoji.maxcdn.com/2/svg/303d.svg"),none}.em-partly_sunny{background-image:url("https://twemoji.maxcdn.com/2/72x72/26c5.png")}.em-partly_sunny.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/26c5.png");background-image:url("https://twemoji.maxcdn.com/2/svg/26c5.svg"),none}.em-partly_sunny_rain{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f326.png")}.em-partly_sunny_rain.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f326.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f326.svg"),none}.em-passenger_ship{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f6f3.png")}.em-passenger_ship.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f6f3.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f6f3.svg"),none}.em-passport_control{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f6c2.png")}.em-passport_control.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f6c2.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f6c2.svg"),none}.em-peace_symbol{background-image:url("https://twemoji.maxcdn.com/2/72x72/262e.png")}.em-peace_symbol.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/262e.png");background-image:url("https://twemoji.maxcdn.com/2/svg/262e.svg"),none}.em-peach{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f351.png")}.em-peach.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f351.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f351.svg"),none}.em-peanuts{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f95c.png")}.em-peanuts.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f95c.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f95c.svg"),none}.em-pear{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f350.png")}.em-pear.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f350.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f350.svg"),none}.em-pencil2{background-image:url("https://twemoji.maxcdn.com/2/72x72/270f.png")}.em-pencil2.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/270f.png");background-image:url("https://twemoji.maxcdn.com/2/svg/270f.svg"),none}.em-penguin{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f427.png")}.em-penguin.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f427.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f427.svg"),none}.em-pensive{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f614.png")}.em-pensive.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f614.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f614.svg"),none}.em-performing_arts{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f3ad.png")}.em-performing_arts.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f3ad.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f3ad.svg"),none}.em-persevere{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f623.png")}.em-persevere.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f623.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f623.svg"),none}.em-person_climbing{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f9d7.png")}.em-person_climbing.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f9d7.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f9d7.svg"),none}.em-person_doing_cartwheel{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f938.png")}.em-person_doing_cartwheel.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f938.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f938.svg"),none}.em-person_frowning{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f64d.png")}.em-person_frowning.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f64d.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f64d.svg"),none}.em-person_in_lotus_position{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f9d8.png")}.em-person_in_lotus_position.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f9d8.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f9d8.svg"),none}.em-person_in_steamy_room{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f9d6.png")}.em-person_in_steamy_room.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f9d6.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f9d6.svg"),none}.em-person_with_ball{background-image:url("https://twemoji.maxcdn.com/2/72x72/26f9.png")}.em-person_with_ball.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/26f9.png");background-image:url("https://twemoji.maxcdn.com/2/svg/26f9.svg"),none}.em-person_with_blond_hair{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f471.png")}.em-person_with_blond_hair.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f471.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f471.svg"),none}.em-person_with_headscarf{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f9d5.png")}.em-person_with_headscarf.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f9d5.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f9d5.svg"),none}.em-person_with_pouting_face{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f64e.png")}.em-person_with_pouting_face.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f64e.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f64e.svg"),none}.em-phone{background-image:url("https://twemoji.maxcdn.com/2/72x72/260e.png")}.em-phone.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/260e.png");background-image:url("https://twemoji.maxcdn.com/2/svg/260e.svg"),none}.em-pick{background-image:url("https://twemoji.maxcdn.com/2/72x72/26cf.png")}.em-pick.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/26cf.png");background-image:url("https://twemoji.maxcdn.com/2/svg/26cf.svg"),none}.em-pie{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f967.png")}.em-pie.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f967.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f967.svg"),none}.em-pig{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f437.png")}.em-pig.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f437.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f437.svg"),none}.em-pig2{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f416.png")}.em-pig2.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f416.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f416.svg"),none}.em-pig_nose{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f43d.png")}.em-pig_nose.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f43d.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f43d.svg"),none}.em-pill{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f48a.png")}.em-pill.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f48a.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f48a.svg"),none}.em-pineapple{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f34d.png")}.em-pineapple.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f34d.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f34d.svg"),none}.em-pisces{background-image:url("https://twemoji.maxcdn.com/2/72x72/2653.png")}.em-pisces.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/2653.png");background-image:url("https://twemoji.maxcdn.com/2/svg/2653.svg"),none}.em-pizza{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f355.png")}.em-pizza.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f355.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f355.svg"),none}.em-place_of_worship{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f6d0.png")}.em-place_of_worship.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f6d0.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f6d0.svg"),none}.em-point_down{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f447.png")}.em-point_down.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f447.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f447.svg"),none}.em-point_left{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f448.png")}.em-point_left.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f448.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f448.svg"),none}.em-point_right{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f449.png")}.em-point_right.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f449.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f449.svg"),none}.em-point_up{background-image:url("https://twemoji.maxcdn.com/2/72x72/261d.png")}.em-point_up.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/261d.png");background-image:url("https://twemoji.maxcdn.com/2/svg/261d.svg"),none}.em-point_up_2{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f446.png")}.em-point_up_2.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f446.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f446.svg"),none}.em-police_car{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f693.png")}.em-police_car.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f693.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f693.svg"),none}.em-poodle{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f429.png")}.em-poodle.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f429.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f429.svg"),none}.em-popcorn{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f37f.png")}.em-popcorn.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f37f.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f37f.svg"),none}.em-post_office{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f3e3.png")}.em-post_office.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f3e3.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f3e3.svg"),none}.em-postal_horn{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f4ef.png")}.em-postal_horn.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f4ef.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f4ef.svg"),none}.em-postbox{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f4ee.png")}.em-postbox.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f4ee.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f4ee.svg"),none}.em-potable_water{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f6b0.png")}.em-potable_water.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f6b0.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f6b0.svg"),none}.em-potato{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f954.png")}.em-potato.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f954.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f954.svg"),none}.em-pouch{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f45d.png")}.em-pouch.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f45d.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f45d.svg"),none}.em-poultry_leg{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f357.png")}.em-poultry_leg.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f357.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f357.svg"),none}.em-pound{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f4b7.png")}.em-pound.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f4b7.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f4b7.svg"),none}.em-pouting_cat{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f63e.png")}.em-pouting_cat.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f63e.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f63e.svg"),none}.em-pray{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f64f.png")}.em-pray.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f64f.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f64f.svg"),none}.em-prayer_beads{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f4ff.png")}.em-prayer_beads.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f4ff.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f4ff.svg"),none}.em-pregnant_woman{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f930.png")}.em-pregnant_woman.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f930.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f930.svg"),none}.em-pretzel{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f968.png")}.em-pretzel.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f968.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f968.svg"),none}.em-prince{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f934.png")}.em-prince.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f934.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f934.svg"),none}.em-princess{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f478.png")}.em-princess.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f478.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f478.svg"),none}.em-printer{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f5a8.png")}.em-printer.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f5a8.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f5a8.svg"),none}.em-purple_heart{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f49c.png")}.em-purple_heart.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f49c.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f49c.svg"),none}.em-purse{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f45b.png")}.em-purse.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f45b.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f45b.svg"),none}.em-pushpin{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f4cc.png")}.em-pushpin.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f4cc.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f4cc.svg"),none}.em-put_litter_in_its_place{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f6ae.png")}.em-put_litter_in_its_place.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f6ae.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f6ae.svg"),none}.em-question{background-image:url("https://twemoji.maxcdn.com/2/72x72/2753.png")}.em-question.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/2753.png");background-image:url("https://twemoji.maxcdn.com/2/svg/2753.svg"),none}.em-rabbit{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f430.png")}.em-rabbit.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f430.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f430.svg"),none}.em-rabbit2{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f407.png")}.em-rabbit2.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f407.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f407.svg"),none}.em-racehorse{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f40e.png")}.em-racehorse.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f40e.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f40e.svg"),none}.em-racing_car{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f3ce.png")}.em-racing_car.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f3ce.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f3ce.svg"),none}.em-racing_motorcycle{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f3cd.png")}.em-racing_motorcycle.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f3cd.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f3cd.svg"),none}.em-radio{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f4fb.png")}.em-radio.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f4fb.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f4fb.svg"),none}.em-radio_button{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f518.png")}.em-radio_button.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f518.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f518.svg"),none}.em-radioactive_sign{background-image:url("https://twemoji.maxcdn.com/2/72x72/2622.png")}.em-radioactive_sign.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/2622.png");background-image:url("https://twemoji.maxcdn.com/2/svg/2622.svg"),none}.em-rage{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f621.png")}.em-rage.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f621.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f621.svg"),none}.em-railway_car{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f683.png")}.em-railway_car.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f683.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f683.svg"),none}.em-railway_track{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f6e4.png")}.em-railway_track.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f6e4.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f6e4.svg"),none}.em-rain_cloud{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f327.png")}.em-rain_cloud.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f327.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f327.svg"),none}.em-rainbow{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f308.png")}.em-rainbow.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f308.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f308.svg"),none}.em-rainbow-flag{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f3f3-fe0f-200d-1f308.png")}.em-rainbow-flag.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f3f3-fe0f-200d-1f308.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f3f3-fe0f-200d-1f308.svg"),none}.em-raised_back_of_hand{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f91a.png")}.em-raised_back_of_hand.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f91a.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f91a.svg"),none}.em-raised_hand_with_fingers_splayed{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f590.png")}.em-raised_hand_with_fingers_splayed.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f590.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f590.svg"),none}.em-raised_hands{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f64c.png")}.em-raised_hands.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f64c.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f64c.svg"),none}.em-raising_hand{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f64b.png")}.em-raising_hand.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f64b.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f64b.svg"),none}.em-ram{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f40f.png")}.em-ram.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f40f.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f40f.svg"),none}.em-ramen{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f35c.png")}.em-ramen.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f35c.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f35c.svg"),none}.em-rat{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f400.png")}.em-rat.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f400.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f400.svg"),none}.em-recycle{background-image:url("https://twemoji.maxcdn.com/2/72x72/267b.png")}.em-recycle.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/267b.png");background-image:url("https://twemoji.maxcdn.com/2/svg/267b.svg"),none}.em-red_circle{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f534.png")}.em-red_circle.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f534.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f534.svg"),none}.em-registered{background-image:url("https://twemoji.maxcdn.com/2/72x72/ae.png")}.em-registered.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/ae.png");background-image:url("https://twemoji.maxcdn.com/2/svg/ae.svg"),none}.em-relaxed{background-image:url("https://twemoji.maxcdn.com/2/72x72/263a.png")}.em-relaxed.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/263a.png");background-image:url("https://twemoji.maxcdn.com/2/svg/263a.svg"),none}.em-relieved{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f60c.png")}.em-relieved.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f60c.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f60c.svg"),none}.em-reminder_ribbon{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f397.png")}.em-reminder_ribbon.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f397.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f397.svg"),none}.em-repeat{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f501.png")}.em-repeat.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f501.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f501.svg"),none}.em-repeat_one{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f502.png")}.em-repeat_one.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f502.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f502.svg"),none}.em-restroom{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f6bb.png")}.em-restroom.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f6bb.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f6bb.svg"),none}.em-revolving_hearts{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f49e.png")}.em-revolving_hearts.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f49e.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f49e.svg"),none}.em-rewind{background-image:url("https://twemoji.maxcdn.com/2/72x72/23ea.png")}.em-rewind.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/23ea.png");background-image:url("https://twemoji.maxcdn.com/2/svg/23ea.svg"),none}.em-rhinoceros{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f98f.png")}.em-rhinoceros.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f98f.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f98f.svg"),none}.em-ribbon{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f380.png")}.em-ribbon.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f380.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f380.svg"),none}.em-rice{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f35a.png")}.em-rice.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f35a.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f35a.svg"),none}.em-rice_ball{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f359.png")}.em-rice_ball.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f359.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f359.svg"),none}.em-rice_cracker{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f358.png")}.em-rice_cracker.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f358.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f358.svg"),none}.em-rice_scene{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f391.png")}.em-rice_scene.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f391.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f391.svg"),none}.em-right-facing_fist{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f91c.png")}.em-right-facing_fist.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f91c.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f91c.svg"),none}.em-right_anger_bubble{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f5ef.png")}.em-right_anger_bubble.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f5ef.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f5ef.svg"),none}.em-ring{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f48d.png")}.em-ring.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f48d.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f48d.svg"),none}.em-robot_face{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f916.png")}.em-robot_face.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f916.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f916.svg"),none}.em-rocket{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f680.png")}.em-rocket.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f680.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f680.svg"),none}.em-rolled_up_newspaper{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f5de.png")}.em-rolled_up_newspaper.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f5de.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f5de.svg"),none}.em-roller_coaster{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f3a2.png")}.em-roller_coaster.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f3a2.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f3a2.svg"),none}.em-rolling_on_the_floor_laughing{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f923.png")}.em-rolling_on_the_floor_laughing.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f923.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f923.svg"),none}.em-rooster{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f413.png")}.em-rooster.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f413.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f413.svg"),none}.em-rose{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f339.png")}.em-rose.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f339.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f339.svg"),none}.em-rosette{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f3f5.png")}.em-rosette.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f3f5.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f3f5.svg"),none}.em-rotating_light{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f6a8.png")}.em-rotating_light.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f6a8.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f6a8.svg"),none}.em-round_pushpin{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f4cd.png")}.em-round_pushpin.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f4cd.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f4cd.svg"),none}.em-rowboat{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f6a3.png")}.em-rowboat.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f6a3.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f6a3.svg"),none}.em-ru{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1f7-1f1fa.png")}.em-ru.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1f7-1f1fa.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1f7-1f1fa.svg"),none}.em-rugby_football{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f3c9.png")}.em-rugby_football.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f3c9.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f3c9.svg"),none}.em-runner{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f3c3.png")}.em-runner.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f3c3.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f3c3.svg"),none}.em-running_shirt_with_sash{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f3bd.png")}.em-running_shirt_with_sash.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f3bd.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f3bd.svg"),none}.em-sa{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f202.png")}.em-sa.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f202.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f202.svg"),none}.em-sagittarius{background-image:url("https://twemoji.maxcdn.com/2/72x72/2650.png")}.em-sagittarius.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/2650.png");background-image:url("https://twemoji.maxcdn.com/2/svg/2650.svg"),none}.em-sake{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f376.png")}.em-sake.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f376.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f376.svg"),none}.em-sandal{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f461.png")}.em-sandal.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f461.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f461.svg"),none}.em-sandwich{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f96a.png")}.em-sandwich.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f96a.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f96a.svg"),none}.em-santa{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f385.png")}.em-santa.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f385.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f385.svg"),none}.em-satellite{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f6f0.png")}.em-satellite.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f6f0.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f6f0.svg"),none}.em-satellite_antenna{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f4e1.png")}.em-satellite_antenna.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f4e1.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f4e1.svg"),none}.em-sauropod{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f995.png")}.em-sauropod.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f995.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f995.svg"),none}.em-saxophone{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f3b7.png")}.em-saxophone.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f3b7.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f3b7.svg"),none}.em-scales{background-image:url("https://twemoji.maxcdn.com/2/72x72/2696.png")}.em-scales.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/2696.png");background-image:url("https://twemoji.maxcdn.com/2/svg/2696.svg"),none}.em-scarf{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f9e3.png")}.em-scarf.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f9e3.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f9e3.svg"),none}.em-school{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f3eb.png")}.em-school.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f3eb.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f3eb.svg"),none}.em-school_satchel{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f392.png")}.em-school_satchel.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f392.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f392.svg"),none}.em-scissors{background-image:url("https://twemoji.maxcdn.com/2/72x72/2702.png")}.em-scissors.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/2702.png");background-image:url("https://twemoji.maxcdn.com/2/svg/2702.svg"),none}.em-scooter{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f6f4.png")}.em-scooter.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f6f4.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f6f4.svg"),none}.em-scorpion{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f982.png")}.em-scorpion.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f982.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f982.svg"),none}.em-scorpius{background-image:url("https://twemoji.maxcdn.com/2/72x72/264f.png")}.em-scorpius.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/264f.png");background-image:url("https://twemoji.maxcdn.com/2/svg/264f.svg"),none}.em-scream{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f631.png")}.em-scream.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f631.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f631.svg"),none}.em-scream_cat{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f640.png")}.em-scream_cat.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f640.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f640.svg"),none}.em-scroll{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f4dc.png")}.em-scroll.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f4dc.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f4dc.svg"),none}.em-seat{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f4ba.png")}.em-seat.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f4ba.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f4ba.svg"),none}.em-second_place_medal{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f948.png")}.em-second_place_medal.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f948.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f948.svg"),none}.em-secret{background-image:url("https://twemoji.maxcdn.com/2/72x72/3299.png")}.em-secret.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/3299.png");background-image:url("https://twemoji.maxcdn.com/2/svg/3299.svg"),none}.em-see_no_evil{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f648.png")}.em-see_no_evil.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f648.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f648.svg"),none}.em-seedling{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f331.png")}.em-seedling.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f331.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f331.svg"),none}.em-selfie{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f933.png")}.em-selfie.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f933.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f933.svg"),none}.em-serious_face_with_symbols_covering_mouth{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f92c.png")}.em-serious_face_with_symbols_covering_mouth.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f92c.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f92c.svg"),none}.em-seven{background-image:url("https://twemoji.maxcdn.com/2/72x72/37-20e3.png")}.em-seven.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/37-20e3.png");background-image:url("https://twemoji.maxcdn.com/2/svg/37-20e3.svg"),none}.em-shallow_pan_of_food{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f958.png")}.em-shallow_pan_of_food.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f958.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f958.svg"),none}.em-shamrock{background-image:url("https://twemoji.maxcdn.com/2/72x72/2618.png")}.em-shamrock.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/2618.png");background-image:url("https://twemoji.maxcdn.com/2/svg/2618.svg"),none}.em-shark{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f988.png")}.em-shark.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f988.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f988.svg"),none}.em-shaved_ice{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f367.png")}.em-shaved_ice.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f367.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f367.svg"),none}.em-sheep{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f411.png")}.em-sheep.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f411.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f411.svg"),none}.em-shell{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f41a.png")}.em-shell.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f41a.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f41a.svg"),none}.em-shield{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f6e1.png")}.em-shield.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f6e1.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f6e1.svg"),none}.em-shinto_shrine{background-image:url("https://twemoji.maxcdn.com/2/72x72/26e9.png")}.em-shinto_shrine.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/26e9.png");background-image:url("https://twemoji.maxcdn.com/2/svg/26e9.svg"),none}.em-ship{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f6a2.png")}.em-ship.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f6a2.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f6a2.svg"),none}.em-shirt{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f455.png")}.em-shirt.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f455.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f455.svg"),none}.em-shocked_face_with_exploding_head{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f92f.png")}.em-shocked_face_with_exploding_head.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f92f.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f92f.svg"),none}.em-shopping_bags{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f6cd.png")}.em-shopping_bags.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f6cd.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f6cd.svg"),none}.em-shopping_trolley{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f6d2.png")}.em-shopping_trolley.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f6d2.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f6d2.svg"),none}.em-shower{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f6bf.png")}.em-shower.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f6bf.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f6bf.svg"),none}.em-shrimp{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f990.png")}.em-shrimp.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f990.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f990.svg"),none}.em-shrug{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f937.png")}.em-shrug.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f937.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f937.svg"),none}.em-signal_strength{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f4f6.png")}.em-signal_strength.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f4f6.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f4f6.svg"),none}.em-six{background-image:url("https://twemoji.maxcdn.com/2/72x72/36-20e3.png")}.em-six.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/36-20e3.png");background-image:url("https://twemoji.maxcdn.com/2/svg/36-20e3.svg"),none}.em-six_pointed_star{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f52f.png")}.em-six_pointed_star.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f52f.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f52f.svg"),none}.em-ski{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f3bf.png")}.em-ski.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f3bf.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f3bf.svg"),none}.em-skier{background-image:url("https://twemoji.maxcdn.com/2/72x72/26f7.png")}.em-skier.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/26f7.png");background-image:url("https://twemoji.maxcdn.com/2/svg/26f7.svg"),none}.em-skin-tone-2{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f3fb.png")}.em-skin-tone-2.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f3fb.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f3fb.svg"),none}.em-skin-tone-3{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f3fc.png")}.em-skin-tone-3.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f3fc.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f3fc.svg"),none}.em-skin-tone-4{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f3fd.png")}.em-skin-tone-4.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f3fd.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f3fd.svg"),none}.em-skin-tone-5{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f3fe.png")}.em-skin-tone-5.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f3fe.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f3fe.svg"),none}.em-skin-tone-6{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f3ff.png")}.em-skin-tone-6.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f3ff.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f3ff.svg"),none}.em-skull{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f480.png")}.em-skull.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f480.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f480.svg"),none}.em-skull_and_crossbones{background-image:url("https://twemoji.maxcdn.com/2/72x72/2620.png")}.em-skull_and_crossbones.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/2620.png");background-image:url("https://twemoji.maxcdn.com/2/svg/2620.svg"),none}.em-sled{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f6f7.png")}.em-sled.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f6f7.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f6f7.svg"),none}.em-sleeping{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f634.png")}.em-sleeping.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f634.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f634.svg"),none}.em-sleeping_accommodation{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f6cc.png")}.em-sleeping_accommodation.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f6cc.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f6cc.svg"),none}.em-sleepy{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f62a.png")}.em-sleepy.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f62a.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f62a.svg"),none}.em-sleuth_or_spy{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f575.png")}.em-sleuth_or_spy.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f575.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f575.svg"),none}.em-slightly_frowning_face{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f641.png")}.em-slightly_frowning_face.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f641.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f641.svg"),none}.em-slightly_smiling_face{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f642.png")}.em-slightly_smiling_face.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f642.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f642.svg"),none}.em-slot_machine{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f3b0.png")}.em-slot_machine.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f3b0.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f3b0.svg"),none}.em-small_airplane{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f6e9.png")}.em-small_airplane.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f6e9.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f6e9.svg"),none}.em-small_blue_diamond{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f539.png")}.em-small_blue_diamond.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f539.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f539.svg"),none}.em-small_orange_diamond{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f538.png")}.em-small_orange_diamond.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f538.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f538.svg"),none}.em-small_red_triangle{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f53a.png")}.em-small_red_triangle.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f53a.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f53a.svg"),none}.em-small_red_triangle_down{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f53b.png")}.em-small_red_triangle_down.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f53b.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f53b.svg"),none}.em-smile{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f604.png")}.em-smile.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f604.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f604.svg"),none}.em-smile_cat{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f638.png")}.em-smile_cat.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f638.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f638.svg"),none}.em-smiley{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f603.png")}.em-smiley.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f603.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f603.svg"),none}.em-smiley_cat{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f63a.png")}.em-smiley_cat.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f63a.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f63a.svg"),none}.em-smiling_face_with_smiling_eyes_and_hand_covering_mouth{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f92d.png")}.em-smiling_face_with_smiling_eyes_and_hand_covering_mouth.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f92d.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f92d.svg"),none}.em-smiling_imp{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f608.png")}.em-smiling_imp.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f608.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f608.svg"),none}.em-smirk{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f60f.png")}.em-smirk.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f60f.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f60f.svg"),none}.em-smirk_cat{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f63c.png")}.em-smirk_cat.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f63c.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f63c.svg"),none}.em-smoking{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f6ac.png")}.em-smoking.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f6ac.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f6ac.svg"),none}.em-snail{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f40c.png")}.em-snail.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f40c.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f40c.svg"),none}.em-snake{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f40d.png")}.em-snake.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f40d.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f40d.svg"),none}.em-sneezing_face{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f927.png")}.em-sneezing_face.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f927.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f927.svg"),none}.em-snow_capped_mountain{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f3d4.png")}.em-snow_capped_mountain.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f3d4.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f3d4.svg"),none}.em-snow_cloud{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f328.png")}.em-snow_cloud.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f328.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f328.svg"),none}.em-snowboarder{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f3c2.png")}.em-snowboarder.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f3c2.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f3c2.svg"),none}.em-snowflake{background-image:url("https://twemoji.maxcdn.com/2/72x72/2744.png")}.em-snowflake.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/2744.png");background-image:url("https://twemoji.maxcdn.com/2/svg/2744.svg"),none}.em-snowman{background-image:url("https://twemoji.maxcdn.com/2/72x72/2603.png")}.em-snowman.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/2603.png");background-image:url("https://twemoji.maxcdn.com/2/svg/2603.svg"),none}.em-snowman_without_snow{background-image:url("https://twemoji.maxcdn.com/2/72x72/26c4.png")}.em-snowman_without_snow.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/26c4.png");background-image:url("https://twemoji.maxcdn.com/2/svg/26c4.svg"),none}.em-sob{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f62d.png")}.em-sob.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f62d.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f62d.svg"),none}.em-soccer{background-image:url("https://twemoji.maxcdn.com/2/72x72/26bd.png")}.em-soccer.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/26bd.png");background-image:url("https://twemoji.maxcdn.com/2/svg/26bd.svg"),none}.em-socks{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f9e6.png")}.em-socks.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f9e6.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f9e6.svg"),none}.em-soon{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f51c.png")}.em-soon.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f51c.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f51c.svg"),none}.em-sos{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f198.png")}.em-sos.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f198.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f198.svg"),none}.em-sound{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f509.png")}.em-sound.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f509.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f509.svg"),none}.em-space_invader{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f47e.png")}.em-space_invader.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f47e.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f47e.svg"),none}.em-spades{background-image:url("https://twemoji.maxcdn.com/2/72x72/2660.png")}.em-spades.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/2660.png");background-image:url("https://twemoji.maxcdn.com/2/svg/2660.svg"),none}.em-spaghetti{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f35d.png")}.em-spaghetti.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f35d.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f35d.svg"),none}.em-sparkle{background-image:url("https://twemoji.maxcdn.com/2/72x72/2747.png")}.em-sparkle.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/2747.png");background-image:url("https://twemoji.maxcdn.com/2/svg/2747.svg"),none}.em-sparkler{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f387.png")}.em-sparkler.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f387.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f387.svg"),none}.em-sparkles{background-image:url("https://twemoji.maxcdn.com/2/72x72/2728.png")}.em-sparkles.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/2728.png");background-image:url("https://twemoji.maxcdn.com/2/svg/2728.svg"),none}.em-sparkling_heart{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f496.png")}.em-sparkling_heart.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f496.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f496.svg"),none}.em-speak_no_evil{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f64a.png")}.em-speak_no_evil.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f64a.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f64a.svg"),none}.em-speaker{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f508.png")}.em-speaker.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f508.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f508.svg"),none}.em-speaking_head_in_silhouette{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f5e3.png")}.em-speaking_head_in_silhouette.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f5e3.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f5e3.svg"),none}.em-speech_balloon{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f4ac.png")}.em-speech_balloon.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f4ac.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f4ac.svg"),none}.em-speedboat{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f6a4.png")}.em-speedboat.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f6a4.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f6a4.svg"),none}.em-spider{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f577.png")}.em-spider.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f577.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f577.svg"),none}.em-spider_web{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f578.png")}.em-spider_web.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f578.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f578.svg"),none}.em-spiral_calendar_pad{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f5d3.png")}.em-spiral_calendar_pad.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f5d3.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f5d3.svg"),none}.em-spiral_note_pad{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f5d2.png")}.em-spiral_note_pad.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f5d2.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f5d2.svg"),none}.em-spock-hand{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f596.png")}.em-spock-hand.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f596.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f596.svg"),none}.em-spoon{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f944.png")}.em-spoon.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f944.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f944.svg"),none}.em-sports_medal{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f3c5.png")}.em-sports_medal.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f3c5.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f3c5.svg"),none}.em-squid{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f991.png")}.em-squid.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f991.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f991.svg"),none}.em-stadium{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f3df.png")}.em-stadium.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f3df.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f3df.svg"),none}.em-staff_of_aesculapius{background-image:url("https://twemoji.maxcdn.com/2/72x72/2695.png")}.em-staff_of_aesculapius.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/2695.png");background-image:url("https://twemoji.maxcdn.com/2/svg/2695.svg"),none}.em-star{background-image:url("https://twemoji.maxcdn.com/2/72x72/2b50.png")}.em-star.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/2b50.png");background-image:url("https://twemoji.maxcdn.com/2/svg/2b50.svg"),none}.em-star2{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f31f.png")}.em-star2.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f31f.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f31f.svg"),none}.em-star_and_crescent{background-image:url("https://twemoji.maxcdn.com/2/72x72/262a.png")}.em-star_and_crescent.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/262a.png");background-image:url("https://twemoji.maxcdn.com/2/svg/262a.svg"),none}.em-star_of_david{background-image:url("https://twemoji.maxcdn.com/2/72x72/2721.png")}.em-star_of_david.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/2721.png");background-image:url("https://twemoji.maxcdn.com/2/svg/2721.svg"),none}.em-stars{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f320.png")}.em-stars.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f320.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f320.svg"),none}.em-station{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f689.png")}.em-station.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f689.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f689.svg"),none}.em-statue_of_liberty{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f5fd.png")}.em-statue_of_liberty.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f5fd.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f5fd.svg"),none}.em-steam_locomotive{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f682.png")}.em-steam_locomotive.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f682.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f682.svg"),none}.em-stew{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f372.png")}.em-stew.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f372.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f372.svg"),none}.em-stopwatch{background-image:url("https://twemoji.maxcdn.com/2/72x72/23f1.png")}.em-stopwatch.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/23f1.png");background-image:url("https://twemoji.maxcdn.com/2/svg/23f1.svg"),none}.em-straight_ruler{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f4cf.png")}.em-straight_ruler.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f4cf.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f4cf.svg"),none}.em-strawberry{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f353.png")}.em-strawberry.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f353.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f353.svg"),none}.em-stuck_out_tongue{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f61b.png")}.em-stuck_out_tongue.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f61b.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f61b.svg"),none}.em-stuck_out_tongue_closed_eyes{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f61d.png")}.em-stuck_out_tongue_closed_eyes.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f61d.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f61d.svg"),none}.em-stuck_out_tongue_winking_eye{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f61c.png")}.em-stuck_out_tongue_winking_eye.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f61c.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f61c.svg"),none}.em-studio_microphone{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f399.png")}.em-studio_microphone.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f399.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f399.svg"),none}.em-stuffed_flatbread{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f959.png")}.em-stuffed_flatbread.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f959.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f959.svg"),none}.em-sun_with_face{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f31e.png")}.em-sun_with_face.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f31e.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f31e.svg"),none}.em-sunflower{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f33b.png")}.em-sunflower.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f33b.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f33b.svg"),none}.em-sunglasses{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f60e.png")}.em-sunglasses.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f60e.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f60e.svg"),none}.em-sunny{background-image:url("https://twemoji.maxcdn.com/2/72x72/2600.png")}.em-sunny.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/2600.png");background-image:url("https://twemoji.maxcdn.com/2/svg/2600.svg"),none}.em-sunrise{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f305.png")}.em-sunrise.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f305.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f305.svg"),none}.em-sunrise_over_mountains{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f304.png")}.em-sunrise_over_mountains.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f304.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f304.svg"),none}.em-surfer{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f3c4.png")}.em-surfer.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f3c4.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f3c4.svg"),none}.em-sushi{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f363.png")}.em-sushi.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f363.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f363.svg"),none}.em-suspension_railway{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f69f.png")}.em-suspension_railway.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f69f.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f69f.svg"),none}.em-sweat{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f613.png")}.em-sweat.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f613.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f613.svg"),none}.em-sweat_drops{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f4a6.png")}.em-sweat_drops.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f4a6.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f4a6.svg"),none}.em-sweat_smile{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f605.png")}.em-sweat_smile.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f605.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f605.svg"),none}.em-sweet_potato{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f360.png")}.em-sweet_potato.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f360.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f360.svg"),none}.em-swimmer{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f3ca.png")}.em-swimmer.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f3ca.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f3ca.svg"),none}.em-symbols{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f523.png")}.em-symbols.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f523.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f523.svg"),none}.em-synagogue{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f54d.png")}.em-synagogue.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f54d.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f54d.svg"),none}.em-syringe{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f489.png")}.em-syringe.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f489.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f489.svg"),none}.em-t-rex{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f996.png")}.em-t-rex.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f996.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f996.svg"),none}.em-table_tennis_paddle_and_ball{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f3d3.png")}.em-table_tennis_paddle_and_ball.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f3d3.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f3d3.svg"),none}.em-taco{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f32e.png")}.em-taco.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f32e.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f32e.svg"),none}.em-tada{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f389.png")}.em-tada.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f389.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f389.svg"),none}.em-takeout_box{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f961.png")}.em-takeout_box.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f961.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f961.svg"),none}.em-tanabata_tree{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f38b.png")}.em-tanabata_tree.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f38b.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f38b.svg"),none}.em-tangerine{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f34a.png")}.em-tangerine.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f34a.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f34a.svg"),none}.em-taurus{background-image:url("https://twemoji.maxcdn.com/2/72x72/2649.png")}.em-taurus.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/2649.png");background-image:url("https://twemoji.maxcdn.com/2/svg/2649.svg"),none}.em-taxi{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f695.png")}.em-taxi.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f695.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f695.svg"),none}.em-tea{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f375.png")}.em-tea.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f375.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f375.svg"),none}.em-telephone_receiver{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f4de.png")}.em-telephone_receiver.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f4de.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f4de.svg"),none}.em-telescope{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f52d.png")}.em-telescope.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f52d.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f52d.svg"),none}.em-tennis{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f3be.png")}.em-tennis.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f3be.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f3be.svg"),none}.em-tent{background-image:url("https://twemoji.maxcdn.com/2/72x72/26fa.png")}.em-tent.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/26fa.png");background-image:url("https://twemoji.maxcdn.com/2/svg/26fa.svg"),none}.em-the_horns{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f918.png")}.em-the_horns.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f918.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f918.svg"),none}.em-thermometer{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f321.png")}.em-thermometer.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f321.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f321.svg"),none}.em-thinking_face{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f914.png")}.em-thinking_face.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f914.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f914.svg"),none}.em-third_place_medal{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f949.png")}.em-third_place_medal.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f949.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f949.svg"),none}.em-thought_balloon{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f4ad.png")}.em-thought_balloon.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f4ad.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f4ad.svg"),none}.em-three{background-image:url("https://twemoji.maxcdn.com/2/72x72/33-20e3.png")}.em-three.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/33-20e3.png");background-image:url("https://twemoji.maxcdn.com/2/svg/33-20e3.svg"),none}.em-three_button_mouse{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f5b1.png")}.em-three_button_mouse.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f5b1.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f5b1.svg"),none}.em-thunder_cloud_and_rain{background-image:url("https://twemoji.maxcdn.com/2/72x72/26c8.png")}.em-thunder_cloud_and_rain.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/26c8.png");background-image:url("https://twemoji.maxcdn.com/2/svg/26c8.svg"),none}.em-ticket{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f3ab.png")}.em-ticket.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f3ab.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f3ab.svg"),none}.em-tiger{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f42f.png")}.em-tiger.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f42f.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f42f.svg"),none}.em-tiger2{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f405.png")}.em-tiger2.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f405.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f405.svg"),none}.em-timer_clock{background-image:url("https://twemoji.maxcdn.com/2/72x72/23f2.png")}.em-timer_clock.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/23f2.png");background-image:url("https://twemoji.maxcdn.com/2/svg/23f2.svg"),none}.em-tired_face{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f62b.png")}.em-tired_face.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f62b.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f62b.svg"),none}.em-tm{background-image:url("https://twemoji.maxcdn.com/2/72x72/2122.png")}.em-tm.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/2122.png");background-image:url("https://twemoji.maxcdn.com/2/svg/2122.svg"),none}.em-toilet{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f6bd.png")}.em-toilet.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f6bd.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f6bd.svg"),none}.em-tokyo_tower{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f5fc.png")}.em-tokyo_tower.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f5fc.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f5fc.svg"),none}.em-tomato{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f345.png")}.em-tomato.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f345.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f345.svg"),none}.em-tongue{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f445.png")}.em-tongue.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f445.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f445.svg"),none}.em-top{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f51d.png")}.em-top.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f51d.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f51d.svg"),none}.em-tophat{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f3a9.png")}.em-tophat.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f3a9.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f3a9.svg"),none}.em-tornado{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f32a.png")}.em-tornado.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f32a.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f32a.svg"),none}.em-trackball{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f5b2.png")}.em-trackball.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f5b2.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f5b2.svg"),none}.em-tractor{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f69c.png")}.em-tractor.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f69c.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f69c.svg"),none}.em-traffic_light{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f6a5.png")}.em-traffic_light.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f6a5.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f6a5.svg"),none}.em-train{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f68b.png")}.em-train.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f68b.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f68b.svg"),none}.em-train2{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f686.png")}.em-train2.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f686.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f686.svg"),none}.em-tram{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f68a.png")}.em-tram.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f68a.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f68a.svg"),none}.em-triangular_flag_on_post{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f6a9.png")}.em-triangular_flag_on_post.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f6a9.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f6a9.svg"),none}.em-triangular_ruler{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f4d0.png")}.em-triangular_ruler.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f4d0.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f4d0.svg"),none}.em-trident{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f531.png")}.em-trident.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f531.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f531.svg"),none}.em-triumph{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f624.png")}.em-triumph.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f624.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f624.svg"),none}.em-trolleybus{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f68e.png")}.em-trolleybus.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f68e.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f68e.svg"),none}.em-trophy{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f3c6.png")}.em-trophy.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f3c6.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f3c6.svg"),none}.em-tropical_drink{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f379.png")}.em-tropical_drink.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f379.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f379.svg"),none}.em-tropical_fish{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f420.png")}.em-tropical_fish.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f420.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f420.svg"),none}.em-truck{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f69a.png")}.em-truck.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f69a.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f69a.svg"),none}.em-trumpet{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f3ba.png")}.em-trumpet.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f3ba.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f3ba.svg"),none}.em-tulip{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f337.png")}.em-tulip.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f337.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f337.svg"),none}.em-tumbler_glass{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f943.png")}.em-tumbler_glass.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f943.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f943.svg"),none}.em-turkey{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f983.png")}.em-turkey.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f983.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f983.svg"),none}.em-turtle{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f422.png")}.em-turtle.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f422.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f422.svg"),none}.em-tv{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f4fa.png")}.em-tv.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f4fa.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f4fa.svg"),none}.em-twisted_rightwards_arrows{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f500.png")}.em-twisted_rightwards_arrows.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f500.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f500.svg"),none}.em-two{background-image:url("https://twemoji.maxcdn.com/2/72x72/32-20e3.png")}.em-two.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/32-20e3.png");background-image:url("https://twemoji.maxcdn.com/2/svg/32-20e3.svg"),none}.em-two_hearts{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f495.png")}.em-two_hearts.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f495.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f495.svg"),none}.em-two_men_holding_hands{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f46c.png")}.em-two_men_holding_hands.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f46c.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f46c.svg"),none}.em-two_women_holding_hands{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f46d.png")}.em-two_women_holding_hands.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f46d.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f46d.svg"),none}.em-u5272{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f239.png")}.em-u5272.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f239.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f239.svg"),none}.em-u5408{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f234.png")}.em-u5408.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f234.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f234.svg"),none}.em-u55b6{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f23a.png")}.em-u55b6.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f23a.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f23a.svg"),none}.em-u6307{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f22f.png")}.em-u6307.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f22f.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f22f.svg"),none}.em-u6708{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f237.png")}.em-u6708.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f237.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f237.svg"),none}.em-u6709{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f236.png")}.em-u6709.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f236.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f236.svg"),none}.em-u6e80{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f235.png")}.em-u6e80.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f235.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f235.svg"),none}.em-u7121{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f21a.png")}.em-u7121.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f21a.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f21a.svg"),none}.em-u7533{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f238.png")}.em-u7533.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f238.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f238.svg"),none}.em-u7981{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f232.png")}.em-u7981.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f232.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f232.svg"),none}.em-u7a7a{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f233.png")}.em-u7a7a.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f233.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f233.svg"),none}.em-umbrella{background-image:url("https://twemoji.maxcdn.com/2/72x72/2602.png")}.em-umbrella.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/2602.png");background-image:url("https://twemoji.maxcdn.com/2/svg/2602.svg"),none}.em-umbrella_on_ground{background-image:url("https://twemoji.maxcdn.com/2/72x72/26f1.png")}.em-umbrella_on_ground.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/26f1.png");background-image:url("https://twemoji.maxcdn.com/2/svg/26f1.svg"),none}.em-umbrella_with_rain_drops{background-image:url("https://twemoji.maxcdn.com/2/72x72/2614.png")}.em-umbrella_with_rain_drops.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/2614.png");background-image:url("https://twemoji.maxcdn.com/2/svg/2614.svg"),none}.em-unamused{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f612.png")}.em-unamused.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f612.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f612.svg"),none}.em-underage{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f51e.png")}.em-underage.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f51e.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f51e.svg"),none}.em-unicorn_face{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f984.png")}.em-unicorn_face.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f984.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f984.svg"),none}.em-unlock{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f513.png")}.em-unlock.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f513.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f513.svg"),none}.em-up{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f199.png")}.em-up.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f199.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f199.svg"),none}.em-upside_down_face{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f643.png")}.em-upside_down_face.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f643.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f643.svg"),none}.em-us{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f1fa-1f1f8.png")}.em-us.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f1fa-1f1f8.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f1fa-1f1f8.svg"),none}.em-v{background-image:url("https://twemoji.maxcdn.com/2/72x72/270c.png")}.em-v.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/270c.png");background-image:url("https://twemoji.maxcdn.com/2/svg/270c.svg"),none}.em-vampire{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f9db.png")}.em-vampire.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f9db.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f9db.svg"),none}.em-vertical_traffic_light{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f6a6.png")}.em-vertical_traffic_light.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f6a6.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f6a6.svg"),none}.em-vhs{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f4fc.png")}.em-vhs.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f4fc.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f4fc.svg"),none}.em-vibration_mode{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f4f3.png")}.em-vibration_mode.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f4f3.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f4f3.svg"),none}.em-video_camera{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f4f9.png")}.em-video_camera.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f4f9.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f4f9.svg"),none}.em-video_game{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f3ae.png")}.em-video_game.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f3ae.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f3ae.svg"),none}.em-violin{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f3bb.png")}.em-violin.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f3bb.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f3bb.svg"),none}.em-virgo{background-image:url("https://twemoji.maxcdn.com/2/72x72/264d.png")}.em-virgo.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/264d.png");background-image:url("https://twemoji.maxcdn.com/2/svg/264d.svg"),none}.em-volcano{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f30b.png")}.em-volcano.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f30b.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f30b.svg"),none}.em-volleyball{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f3d0.png")}.em-volleyball.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f3d0.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f3d0.svg"),none}.em-vs{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f19a.png")}.em-vs.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f19a.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f19a.svg"),none}.em-walking{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f6b6.png")}.em-walking.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f6b6.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f6b6.svg"),none}.em-waning_crescent_moon{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f318.png")}.em-waning_crescent_moon.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f318.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f318.svg"),none}.em-waning_gibbous_moon{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f316.png")}.em-waning_gibbous_moon.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f316.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f316.svg"),none}.em-warning{background-image:url("https://twemoji.maxcdn.com/2/72x72/26a0.png")}.em-warning.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/26a0.png");background-image:url("https://twemoji.maxcdn.com/2/svg/26a0.svg"),none}.em-wastebasket{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f5d1.png")}.em-wastebasket.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f5d1.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f5d1.svg"),none}.em-watch{background-image:url("https://twemoji.maxcdn.com/2/72x72/231a.png")}.em-watch.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/231a.png");background-image:url("https://twemoji.maxcdn.com/2/svg/231a.svg"),none}.em-water_buffalo{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f403.png")}.em-water_buffalo.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f403.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f403.svg"),none}.em-water_polo{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f93d.png")}.em-water_polo.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f93d.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f93d.svg"),none}.em-watermelon{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f349.png")}.em-watermelon.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f349.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f349.svg"),none}.em-wave{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f44b.png")}.em-wave.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f44b.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f44b.svg"),none}.em-waving_black_flag{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f3f4.png")}.em-waving_black_flag.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f3f4.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f3f4.svg"),none}.em-waving_white_flag{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f3f3.png")}.em-waving_white_flag.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f3f3.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f3f3.svg"),none}.em-wavy_dash{background-image:url("https://twemoji.maxcdn.com/2/72x72/3030.png")}.em-wavy_dash.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/3030.png");background-image:url("https://twemoji.maxcdn.com/2/svg/3030.svg"),none}.em-waxing_crescent_moon{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f312.png")}.em-waxing_crescent_moon.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f312.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f312.svg"),none}.em-wc{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f6be.png")}.em-wc.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f6be.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f6be.svg"),none}.em-weary{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f629.png")}.em-weary.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f629.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f629.svg"),none}.em-wedding{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f492.png")}.em-wedding.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f492.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f492.svg"),none}.em-weight_lifter{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f3cb.png")}.em-weight_lifter.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f3cb.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f3cb.svg"),none}.em-whale{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f433.png")}.em-whale.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f433.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f433.svg"),none}.em-whale2{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f40b.png")}.em-whale2.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f40b.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f40b.svg"),none}.em-wheel_of_dharma{background-image:url("https://twemoji.maxcdn.com/2/72x72/2638.png")}.em-wheel_of_dharma.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/2638.png");background-image:url("https://twemoji.maxcdn.com/2/svg/2638.svg"),none}.em-wheelchair{background-image:url("https://twemoji.maxcdn.com/2/72x72/267f.png")}.em-wheelchair.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/267f.png");background-image:url("https://twemoji.maxcdn.com/2/svg/267f.svg"),none}.em-white_check_mark{background-image:url("https://twemoji.maxcdn.com/2/72x72/2705.png")}.em-white_check_mark.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/2705.png");background-image:url("https://twemoji.maxcdn.com/2/svg/2705.svg"),none}.em-white_circle{background-image:url("https://twemoji.maxcdn.com/2/72x72/26aa.png")}.em-white_circle.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/26aa.png");background-image:url("https://twemoji.maxcdn.com/2/svg/26aa.svg"),none}.em-white_flower{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f4ae.png")}.em-white_flower.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f4ae.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f4ae.svg"),none}.em-white_frowning_face{background-image:url("https://twemoji.maxcdn.com/2/72x72/2639.png")}.em-white_frowning_face.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/2639.png");background-image:url("https://twemoji.maxcdn.com/2/svg/2639.svg"),none}.em-white_large_square{background-image:url("https://twemoji.maxcdn.com/2/72x72/2b1c.png")}.em-white_large_square.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/2b1c.png");background-image:url("https://twemoji.maxcdn.com/2/svg/2b1c.svg"),none}.em-white_medium_small_square{background-image:url("https://twemoji.maxcdn.com/2/72x72/25fd.png")}.em-white_medium_small_square.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/25fd.png");background-image:url("https://twemoji.maxcdn.com/2/svg/25fd.svg"),none}.em-white_medium_square{background-image:url("https://twemoji.maxcdn.com/2/72x72/25fb.png")}.em-white_medium_square.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/25fb.png");background-image:url("https://twemoji.maxcdn.com/2/svg/25fb.svg"),none}.em-white_small_square{background-image:url("https://twemoji.maxcdn.com/2/72x72/25ab.png")}.em-white_small_square.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/25ab.png");background-image:url("https://twemoji.maxcdn.com/2/svg/25ab.svg"),none}.em-white_square_button{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f533.png")}.em-white_square_button.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f533.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f533.svg"),none}.em-wilted_flower{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f940.png")}.em-wilted_flower.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f940.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f940.svg"),none}.em-wind_blowing_face{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f32c.png")}.em-wind_blowing_face.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f32c.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f32c.svg"),none}.em-wind_chime{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f390.png")}.em-wind_chime.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f390.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f390.svg"),none}.em-wine_glass{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f377.png")}.em-wine_glass.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f377.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f377.svg"),none}.em-wink{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f609.png")}.em-wink.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f609.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f609.svg"),none}.em-wolf{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f43a.png")}.em-wolf.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f43a.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f43a.svg"),none}.em-woman{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f469.png")}.em-woman.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f469.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f469.svg"),none}.em-woman-biking{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f6b4-200d-2640-fe0f.png")}.em-woman-biking.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f6b4-200d-2640-fe0f.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f6b4-200d-2640-fe0f.svg"),none}.em-woman-bouncing-ball{background-image:url("https://twemoji.maxcdn.com/2/72x72/26f9-fe0f-200d-2640-fe0f.png")}.em-woman-bouncing-ball.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/26f9-fe0f-200d-2640-fe0f.png");background-image:url("https://twemoji.maxcdn.com/2/svg/26f9-fe0f-200d-2640-fe0f.svg"),none}.em-woman-bowing{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f647-200d-2640-fe0f.png")}.em-woman-bowing.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f647-200d-2640-fe0f.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f647-200d-2640-fe0f.svg"),none}.em-woman-boy{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f469-200d-1f466.png")}.em-woman-boy.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f469-200d-1f466.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f469-200d-1f466.svg"),none}.em-woman-boy-boy{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f469-200d-1f466-200d-1f466.png")}.em-woman-boy-boy.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f469-200d-1f466-200d-1f466.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f469-200d-1f466-200d-1f466.svg"),none}.em-woman-cartwheeling{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f938-200d-2640-fe0f.png")}.em-woman-cartwheeling.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f938-200d-2640-fe0f.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f938-200d-2640-fe0f.svg"),none}.em-woman-facepalming{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f926-200d-2640-fe0f.png")}.em-woman-facepalming.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f926-200d-2640-fe0f.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f926-200d-2640-fe0f.svg"),none}.em-woman-frowning{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f64d-200d-2640-fe0f.png")}.em-woman-frowning.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f64d-200d-2640-fe0f.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f64d-200d-2640-fe0f.svg"),none}.em-woman-gesturing-no{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f645-200d-2640-fe0f.png")}.em-woman-gesturing-no.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f645-200d-2640-fe0f.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f645-200d-2640-fe0f.svg"),none}.em-woman-gesturing-ok{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f646-200d-2640-fe0f.png")}.em-woman-gesturing-ok.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f646-200d-2640-fe0f.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f646-200d-2640-fe0f.svg"),none}.em-woman-getting-haircut{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f487-200d-2640-fe0f.png")}.em-woman-getting-haircut.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f487-200d-2640-fe0f.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f487-200d-2640-fe0f.svg"),none}.em-woman-getting-massage{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f486-200d-2640-fe0f.png")}.em-woman-getting-massage.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f486-200d-2640-fe0f.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f486-200d-2640-fe0f.svg"),none}.em-woman-girl{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f469-200d-1f467.png")}.em-woman-girl.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f469-200d-1f467.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f469-200d-1f467.svg"),none}.em-woman-girl-boy{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f469-200d-1f467-200d-1f466.png")}.em-woman-girl-boy.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f469-200d-1f467-200d-1f466.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f469-200d-1f467-200d-1f466.svg"),none}.em-woman-girl-girl{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f469-200d-1f467-200d-1f467.png")}.em-woman-girl-girl.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f469-200d-1f467-200d-1f467.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f469-200d-1f467-200d-1f467.svg"),none}.em-woman-golfing{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f3cc-fe0f-200d-2640-fe0f.png")}.em-woman-golfing.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f3cc-fe0f-200d-2640-fe0f.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f3cc-fe0f-200d-2640-fe0f.svg"),none}.em-woman-heart-man{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f469-200d-2764-fe0f-200d-1f468.png")}.em-woman-heart-man.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f469-200d-2764-fe0f-200d-1f468.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f469-200d-2764-fe0f-200d-1f468.svg"),none}.em-woman-heart-woman{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f469-200d-2764-fe0f-200d-1f469.png")}.em-woman-heart-woman.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f469-200d-2764-fe0f-200d-1f469.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f469-200d-2764-fe0f-200d-1f469.svg"),none}.em-woman-juggling{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f939-200d-2640-fe0f.png")}.em-woman-juggling.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f939-200d-2640-fe0f.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f939-200d-2640-fe0f.svg"),none}.em-woman-kiss-man{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f469-200d-2764-fe0f-200d-1f48b-200d-1f468.png")}.em-woman-kiss-man.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f469-200d-2764-fe0f-200d-1f48b-200d-1f468.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f469-200d-2764-fe0f-200d-1f48b-200d-1f468.svg"),none}.em-woman-kiss-woman{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f469-200d-2764-fe0f-200d-1f48b-200d-1f469.png")}.em-woman-kiss-woman.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f469-200d-2764-fe0f-200d-1f48b-200d-1f469.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f469-200d-2764-fe0f-200d-1f48b-200d-1f469.svg"),none}.em-woman-lifting-weights{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f3cb-fe0f-200d-2640-fe0f.png")}.em-woman-lifting-weights.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f3cb-fe0f-200d-2640-fe0f.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f3cb-fe0f-200d-2640-fe0f.svg"),none}.em-woman-mountain-biking{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f6b5-200d-2640-fe0f.png")}.em-woman-mountain-biking.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f6b5-200d-2640-fe0f.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f6b5-200d-2640-fe0f.svg"),none}.em-woman-playing-handball{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f93e-200d-2640-fe0f.png")}.em-woman-playing-handball.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f93e-200d-2640-fe0f.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f93e-200d-2640-fe0f.svg"),none}.em-woman-playing-water-polo{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f93d-200d-2640-fe0f.png")}.em-woman-playing-water-polo.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f93d-200d-2640-fe0f.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f93d-200d-2640-fe0f.svg"),none}.em-woman-pouting{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f64e-200d-2640-fe0f.png")}.em-woman-pouting.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f64e-200d-2640-fe0f.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f64e-200d-2640-fe0f.svg"),none}.em-woman-raising-hand{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f64b-200d-2640-fe0f.png")}.em-woman-raising-hand.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f64b-200d-2640-fe0f.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f64b-200d-2640-fe0f.svg"),none}.em-woman-rowing-boat{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f6a3-200d-2640-fe0f.png")}.em-woman-rowing-boat.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f6a3-200d-2640-fe0f.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f6a3-200d-2640-fe0f.svg"),none}.em-woman-running{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f3c3-200d-2640-fe0f.png")}.em-woman-running.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f3c3-200d-2640-fe0f.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f3c3-200d-2640-fe0f.svg"),none}.em-woman-shrugging{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f937-200d-2640-fe0f.png")}.em-woman-shrugging.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f937-200d-2640-fe0f.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f937-200d-2640-fe0f.svg"),none}.em-woman-surfing{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f3c4-200d-2640-fe0f.png")}.em-woman-surfing.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f3c4-200d-2640-fe0f.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f3c4-200d-2640-fe0f.svg"),none}.em-woman-swimming{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f3ca-200d-2640-fe0f.png")}.em-woman-swimming.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f3ca-200d-2640-fe0f.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f3ca-200d-2640-fe0f.svg"),none}.em-woman-tipping-hand{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f481-200d-2640-fe0f.png")}.em-woman-tipping-hand.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f481-200d-2640-fe0f.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f481-200d-2640-fe0f.svg"),none}.em-woman-walking{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f6b6-200d-2640-fe0f.png")}.em-woman-walking.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f6b6-200d-2640-fe0f.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f6b6-200d-2640-fe0f.svg"),none}.em-woman-wearing-turban{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f473-200d-2640-fe0f.png")}.em-woman-wearing-turban.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f473-200d-2640-fe0f.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f473-200d-2640-fe0f.svg"),none}.em-woman-with-bunny-ears-partying{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f46f-200d-2640-fe0f.png")}.em-woman-with-bunny-ears-partying.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f46f-200d-2640-fe0f.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f46f-200d-2640-fe0f.svg"),none}.em-woman-woman-boy{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f469-200d-1f469-200d-1f466.png")}.em-woman-woman-boy.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f469-200d-1f469-200d-1f466.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f469-200d-1f469-200d-1f466.svg"),none}.em-woman-woman-boy-boy{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f469-200d-1f469-200d-1f466-200d-1f466.png")}.em-woman-woman-boy-boy.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f469-200d-1f469-200d-1f466-200d-1f466.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f469-200d-1f469-200d-1f466-200d-1f466.svg"),none}.em-woman-woman-girl{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f469-200d-1f469-200d-1f467.png")}.em-woman-woman-girl.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f469-200d-1f469-200d-1f467.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f469-200d-1f469-200d-1f467.svg"),none}.em-woman-woman-girl-boy{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f469-200d-1f469-200d-1f467-200d-1f466.png")}.em-woman-woman-girl-boy.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f469-200d-1f469-200d-1f467-200d-1f466.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f469-200d-1f469-200d-1f467-200d-1f466.svg"),none}.em-woman-woman-girl-girl{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f469-200d-1f469-200d-1f467-200d-1f467.png")}.em-woman-woman-girl-girl.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f469-200d-1f469-200d-1f467-200d-1f467.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f469-200d-1f469-200d-1f467-200d-1f467.svg"),none}.em-woman-wrestling{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f93c-200d-2640-fe0f.png")}.em-woman-wrestling.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f93c-200d-2640-fe0f.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f93c-200d-2640-fe0f.svg"),none}.em-woman_climbing{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f9d7-200d-2640-fe0f.png")}.em-woman_climbing.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f9d7-200d-2640-fe0f.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f9d7-200d-2640-fe0f.svg"),none}.em-woman_in_lotus_position{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f9d8-200d-2640-fe0f.png")}.em-woman_in_lotus_position.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f9d8-200d-2640-fe0f.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f9d8-200d-2640-fe0f.svg"),none}.em-woman_in_steamy_room{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f9d6-200d-2640-fe0f.png")}.em-woman_in_steamy_room.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f9d6-200d-2640-fe0f.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f9d6-200d-2640-fe0f.svg"),none}.em-womans_clothes{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f45a.png")}.em-womans_clothes.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f45a.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f45a.svg"),none}.em-womans_hat{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f452.png")}.em-womans_hat.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f452.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f452.svg"),none}.em-womens{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f6ba.png")}.em-womens.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f6ba.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f6ba.svg"),none}.em-world_map{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f5fa.png")}.em-world_map.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f5fa.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f5fa.svg"),none}.em-worried{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f61f.png")}.em-worried.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f61f.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f61f.svg"),none}.em-wrench{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f527.png")}.em-wrench.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f527.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f527.svg"),none}.em-wrestlers{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f93c.png")}.em-wrestlers.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f93c.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f93c.svg"),none}.em-writing_hand{background-image:url("https://twemoji.maxcdn.com/2/72x72/270d.png")}.em-writing_hand.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/270d.png");background-image:url("https://twemoji.maxcdn.com/2/svg/270d.svg"),none}.em-x{background-image:url("https://twemoji.maxcdn.com/2/72x72/274c.png")}.em-x.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/274c.png");background-image:url("https://twemoji.maxcdn.com/2/svg/274c.svg"),none}.em-yellow_heart{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f49b.png")}.em-yellow_heart.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f49b.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f49b.svg"),none}.em-yen{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f4b4.png")}.em-yen.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f4b4.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f4b4.svg"),none}.em-yin_yang{background-image:url("https://twemoji.maxcdn.com/2/72x72/262f.png")}.em-yin_yang.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/262f.png");background-image:url("https://twemoji.maxcdn.com/2/svg/262f.svg"),none}.em-yum{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f60b.png")}.em-yum.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f60b.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f60b.svg"),none}.em-zap{background-image:url("https://twemoji.maxcdn.com/2/72x72/26a1.png")}.em-zap.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/26a1.png");background-image:url("https://twemoji.maxcdn.com/2/svg/26a1.svg"),none}.em-zebra_face{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f993.png")}.em-zebra_face.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f993.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f993.svg"),none}.em-zero{background-image:url("https://twemoji.maxcdn.com/2/72x72/30-20e3.png")}.em-zero.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/30-20e3.png");background-image:url("https://twemoji.maxcdn.com/2/svg/30-20e3.svg"),none}.em-zipper_mouth_face{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f910.png")}.em-zipper_mouth_face.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f910.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f910.svg"),none}.em-zombie{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f9df.png")}.em-zombie.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f9df.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f9df.svg"),none}.em-zzz{background-image:url("https://twemoji.maxcdn.com/2/72x72/1f4a4.png")}.em-zzz.em-svg{background:url("https://twemoji.maxcdn.com/2/72x72/1f4a4.png");background-image:url("https://twemoji.maxcdn.com/2/svg/1f4a4.svg"),none} diff --git a/public/js/app/page.js b/public/js/app/page.js index 1ece45a19..4bd6330e8 100644 --- a/public/js/app/page.js +++ b/public/js/app/page.js @@ -471,13 +471,13 @@ function initEditor() { // skin : "custom", language: Api.curLang.indexOf('zh') >= 0 ? 'zh' : 'en', // 语言 plugins: [ - "autolink link image leaui_mindmap lists charmap hr", "paste", + "code autolink link image leaui_mindmap lists charmap hr", "paste", "searchreplace leanote_nav leanote_code tabfocus", "table directionality textcolor" ], // nonbreaking toolbar1: "formatselect | forecolor backcolor | bold italic underline strikethrough | image leaui_mindmap | leanote_code leanote_inline_code | bullist numlist | alignleft aligncenter alignright alignjustify", - toolbar2: "outdent indent blockquote | link unlink | table | hr removeformat | subscript superscript |searchreplace | pastetext | leanote_ace_pre | fontselect fontsizeselect", + toolbar2: "outdent indent blockquote | link unlink | table | hr removeformat | subscript superscript | searchreplace | pastetext | leanote_ace_pre code | fontselect fontsizeselect", // 使用tab键: http://www.tinymce.com/wiki.php/Plugin3x:nonbreaking // http://stackoverflow.com/questions/13543220/tiny-mce-how-to-allow-people-to-indent diff --git a/public/js/app/tag.js b/public/js/app/tag.js index 3951c90af..ae2b64e21 100644 --- a/public/js/app/tag.js +++ b/public/js/app/tag.js @@ -99,6 +99,11 @@ TagNav.prototype = { // 更新tags,并重绘到左侧 setTags: function(tags) { this.tags = tags || []; + // wdd add start + this.tags.sort(function(a, b){ + return a.Tag.toLowerCase() > b.Tag.toLowerCase() ? 1 : -1; +}); +// wdd add end $('#tagNav').html(''); for(var i in this.tags) { var noteTag = this.tags[i]; diff --git a/public/js/chart.js b/public/js/chart.js new file mode 100644 index 000000000..32c747fc5 --- /dev/null +++ b/public/js/chart.js @@ -0,0 +1,14145 @@ +/*! + * Chart.js + * http://chartjs.org/ + * Version: 2.7.1 + * + * Copyright 2017 Nick Downie + * Released under the MIT license + * https://github.com/chartjs/Chart.js/blob/master/LICENSE.md + */ +(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.Chart = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o lum2) { + return (lum1 + 0.05) / (lum2 + 0.05); + } + return (lum2 + 0.05) / (lum1 + 0.05); + }, + + level: function (color2) { + var contrastRatio = this.contrast(color2); + if (contrastRatio >= 7.1) { + return 'AAA'; + } + + return (contrastRatio >= 4.5) ? 'AA' : ''; + }, + + dark: function () { + // YIQ equation from http://24ways.org/2010/calculating-color-contrast + var rgb = this.values.rgb; + var yiq = (rgb[0] * 299 + rgb[1] * 587 + rgb[2] * 114) / 1000; + return yiq < 128; + }, + + light: function () { + return !this.dark(); + }, + + negate: function () { + var rgb = []; + for (var i = 0; i < 3; i++) { + rgb[i] = 255 - this.values.rgb[i]; + } + this.setValues('rgb', rgb); + return this; + }, + + lighten: function (ratio) { + var hsl = this.values.hsl; + hsl[2] += hsl[2] * ratio; + this.setValues('hsl', hsl); + return this; + }, + + darken: function (ratio) { + var hsl = this.values.hsl; + hsl[2] -= hsl[2] * ratio; + this.setValues('hsl', hsl); + return this; + }, + + saturate: function (ratio) { + var hsl = this.values.hsl; + hsl[1] += hsl[1] * ratio; + this.setValues('hsl', hsl); + return this; + }, + + desaturate: function (ratio) { + var hsl = this.values.hsl; + hsl[1] -= hsl[1] * ratio; + this.setValues('hsl', hsl); + return this; + }, + + whiten: function (ratio) { + var hwb = this.values.hwb; + hwb[1] += hwb[1] * ratio; + this.setValues('hwb', hwb); + return this; + }, + + blacken: function (ratio) { + var hwb = this.values.hwb; + hwb[2] += hwb[2] * ratio; + this.setValues('hwb', hwb); + return this; + }, + + greyscale: function () { + var rgb = this.values.rgb; + // http://en.wikipedia.org/wiki/Grayscale#Converting_color_to_grayscale + var val = rgb[0] * 0.3 + rgb[1] * 0.59 + rgb[2] * 0.11; + this.setValues('rgb', [val, val, val]); + return this; + }, + + clearer: function (ratio) { + var alpha = this.values.alpha; + this.setValues('alpha', alpha - (alpha * ratio)); + return this; + }, + + opaquer: function (ratio) { + var alpha = this.values.alpha; + this.setValues('alpha', alpha + (alpha * ratio)); + return this; + }, + + rotate: function (degrees) { + var hsl = this.values.hsl; + var hue = (hsl[0] + degrees) % 360; + hsl[0] = hue < 0 ? 360 + hue : hue; + this.setValues('hsl', hsl); + return this; + }, + + /** + * Ported from sass implementation in C + * https://github.com/sass/libsass/blob/0e6b4a2850092356aa3ece07c6b249f0221caced/functions.cpp#L209 + */ + mix: function (mixinColor, weight) { + var color1 = this; + var color2 = mixinColor; + var p = weight === undefined ? 0.5 : weight; + + var w = 2 * p - 1; + var a = color1.alpha() - color2.alpha(); + + var w1 = (((w * a === -1) ? w : (w + a) / (1 + w * a)) + 1) / 2.0; + var w2 = 1 - w1; + + return this + .rgb( + w1 * color1.red() + w2 * color2.red(), + w1 * color1.green() + w2 * color2.green(), + w1 * color1.blue() + w2 * color2.blue() + ) + .alpha(color1.alpha() * p + color2.alpha() * (1 - p)); + }, + + toJSON: function () { + return this.rgb(); + }, + + clone: function () { + // NOTE(SB): using node-clone creates a dependency to Buffer when using browserify, + // making the final build way to big to embed in Chart.js. So let's do it manually, + // assuming that values to clone are 1 dimension arrays containing only numbers, + // except 'alpha' which is a number. + var result = new Color(); + var source = this.values; + var target = result.values; + var value, type; + + for (var prop in source) { + if (source.hasOwnProperty(prop)) { + value = source[prop]; + type = ({}).toString.call(value); + if (type === '[object Array]') { + target[prop] = value.slice(0); + } else if (type === '[object Number]') { + target[prop] = value; + } else { + console.error('unexpected color value:', value); + } + } + } + + return result; + } +}; + +Color.prototype.spaces = { + rgb: ['red', 'green', 'blue'], + hsl: ['hue', 'saturation', 'lightness'], + hsv: ['hue', 'saturation', 'value'], + hwb: ['hue', 'whiteness', 'blackness'], + cmyk: ['cyan', 'magenta', 'yellow', 'black'] +}; + +Color.prototype.maxes = { + rgb: [255, 255, 255], + hsl: [360, 100, 100], + hsv: [360, 100, 100], + hwb: [360, 100, 100], + cmyk: [100, 100, 100, 100] +}; + +Color.prototype.getValues = function (space) { + var values = this.values; + var vals = {}; + + for (var i = 0; i < space.length; i++) { + vals[space.charAt(i)] = values[space][i]; + } + + if (values.alpha !== 1) { + vals.a = values.alpha; + } + + // {r: 255, g: 255, b: 255, a: 0.4} + return vals; +}; + +Color.prototype.setValues = function (space, vals) { + var values = this.values; + var spaces = this.spaces; + var maxes = this.maxes; + var alpha = 1; + var i; + + this.valid = true; + + if (space === 'alpha') { + alpha = vals; + } else if (vals.length) { + // [10, 10, 10] + values[space] = vals.slice(0, space.length); + alpha = vals[space.length]; + } else if (vals[space.charAt(0)] !== undefined) { + // {r: 10, g: 10, b: 10} + for (i = 0; i < space.length; i++) { + values[space][i] = vals[space.charAt(i)]; + } + + alpha = vals.a; + } else if (vals[spaces[space][0]] !== undefined) { + // {red: 10, green: 10, blue: 10} + var chans = spaces[space]; + + for (i = 0; i < space.length; i++) { + values[space][i] = vals[chans[i]]; + } + + alpha = vals.alpha; + } + + values.alpha = Math.max(0, Math.min(1, (alpha === undefined ? values.alpha : alpha))); + + if (space === 'alpha') { + return false; + } + + var capped; + + // cap values of the space prior converting all values + for (i = 0; i < space.length; i++) { + capped = Math.max(0, Math.min(maxes[space][i], values[space][i])); + values[space][i] = Math.round(capped); + } + + // convert to all the other color spaces + for (var sname in spaces) { + if (sname !== space) { + values[sname] = convert[space][sname](values[space]); + } + } + + return true; +}; + +Color.prototype.setSpace = function (space, args) { + var vals = args[0]; + + if (vals === undefined) { + // color.rgb() + return this.getValues(space); + } + + // color.rgb(10, 10, 10) + if (typeof vals === 'number') { + vals = Array.prototype.slice.call(args); + } + + this.setValues(space, vals); + return this; +}; + +Color.prototype.setChannel = function (space, index, val) { + var svalues = this.values[space]; + if (val === undefined) { + // color.red() + return svalues[index]; + } else if (val === svalues[index]) { + // color.red(color.red()) + return this; + } + + // color.red(100) + svalues[index] = val; + this.setValues(space, svalues); + + return this; +}; + +if (typeof window !== 'undefined') { + window.Color = Color; +} + +module.exports = Color; + +},{"2":2,"5":5}],4:[function(require,module,exports){ +/* MIT license */ + +module.exports = { + rgb2hsl: rgb2hsl, + rgb2hsv: rgb2hsv, + rgb2hwb: rgb2hwb, + rgb2cmyk: rgb2cmyk, + rgb2keyword: rgb2keyword, + rgb2xyz: rgb2xyz, + rgb2lab: rgb2lab, + rgb2lch: rgb2lch, + + hsl2rgb: hsl2rgb, + hsl2hsv: hsl2hsv, + hsl2hwb: hsl2hwb, + hsl2cmyk: hsl2cmyk, + hsl2keyword: hsl2keyword, + + hsv2rgb: hsv2rgb, + hsv2hsl: hsv2hsl, + hsv2hwb: hsv2hwb, + hsv2cmyk: hsv2cmyk, + hsv2keyword: hsv2keyword, + + hwb2rgb: hwb2rgb, + hwb2hsl: hwb2hsl, + hwb2hsv: hwb2hsv, + hwb2cmyk: hwb2cmyk, + hwb2keyword: hwb2keyword, + + cmyk2rgb: cmyk2rgb, + cmyk2hsl: cmyk2hsl, + cmyk2hsv: cmyk2hsv, + cmyk2hwb: cmyk2hwb, + cmyk2keyword: cmyk2keyword, + + keyword2rgb: keyword2rgb, + keyword2hsl: keyword2hsl, + keyword2hsv: keyword2hsv, + keyword2hwb: keyword2hwb, + keyword2cmyk: keyword2cmyk, + keyword2lab: keyword2lab, + keyword2xyz: keyword2xyz, + + xyz2rgb: xyz2rgb, + xyz2lab: xyz2lab, + xyz2lch: xyz2lch, + + lab2xyz: lab2xyz, + lab2rgb: lab2rgb, + lab2lch: lab2lch, + + lch2lab: lch2lab, + lch2xyz: lch2xyz, + lch2rgb: lch2rgb +} + + +function rgb2hsl(rgb) { + var r = rgb[0]/255, + g = rgb[1]/255, + b = rgb[2]/255, + min = Math.min(r, g, b), + max = Math.max(r, g, b), + delta = max - min, + h, s, l; + + if (max == min) + h = 0; + else if (r == max) + h = (g - b) / delta; + else if (g == max) + h = 2 + (b - r) / delta; + else if (b == max) + h = 4 + (r - g)/ delta; + + h = Math.min(h * 60, 360); + + if (h < 0) + h += 360; + + l = (min + max) / 2; + + if (max == min) + s = 0; + else if (l <= 0.5) + s = delta / (max + min); + else + s = delta / (2 - max - min); + + return [h, s * 100, l * 100]; +} + +function rgb2hsv(rgb) { + var r = rgb[0], + g = rgb[1], + b = rgb[2], + min = Math.min(r, g, b), + max = Math.max(r, g, b), + delta = max - min, + h, s, v; + + if (max == 0) + s = 0; + else + s = (delta/max * 1000)/10; + + if (max == min) + h = 0; + else if (r == max) + h = (g - b) / delta; + else if (g == max) + h = 2 + (b - r) / delta; + else if (b == max) + h = 4 + (r - g) / delta; + + h = Math.min(h * 60, 360); + + if (h < 0) + h += 360; + + v = ((max / 255) * 1000) / 10; + + return [h, s, v]; +} + +function rgb2hwb(rgb) { + var r = rgb[0], + g = rgb[1], + b = rgb[2], + h = rgb2hsl(rgb)[0], + w = 1/255 * Math.min(r, Math.min(g, b)), + b = 1 - 1/255 * Math.max(r, Math.max(g, b)); + + return [h, w * 100, b * 100]; +} + +function rgb2cmyk(rgb) { + var r = rgb[0] / 255, + g = rgb[1] / 255, + b = rgb[2] / 255, + c, m, y, k; + + k = Math.min(1 - r, 1 - g, 1 - b); + c = (1 - r - k) / (1 - k) || 0; + m = (1 - g - k) / (1 - k) || 0; + y = (1 - b - k) / (1 - k) || 0; + return [c * 100, m * 100, y * 100, k * 100]; +} + +function rgb2keyword(rgb) { + return reverseKeywords[JSON.stringify(rgb)]; +} + +function rgb2xyz(rgb) { + var r = rgb[0] / 255, + g = rgb[1] / 255, + b = rgb[2] / 255; + + // assume sRGB + r = r > 0.04045 ? Math.pow(((r + 0.055) / 1.055), 2.4) : (r / 12.92); + g = g > 0.04045 ? Math.pow(((g + 0.055) / 1.055), 2.4) : (g / 12.92); + b = b > 0.04045 ? Math.pow(((b + 0.055) / 1.055), 2.4) : (b / 12.92); + + var x = (r * 0.4124) + (g * 0.3576) + (b * 0.1805); + var y = (r * 0.2126) + (g * 0.7152) + (b * 0.0722); + var z = (r * 0.0193) + (g * 0.1192) + (b * 0.9505); + + return [x * 100, y *100, z * 100]; +} + +function rgb2lab(rgb) { + var xyz = rgb2xyz(rgb), + x = xyz[0], + y = xyz[1], + z = xyz[2], + l, a, b; + + x /= 95.047; + y /= 100; + z /= 108.883; + + x = x > 0.008856 ? Math.pow(x, 1/3) : (7.787 * x) + (16 / 116); + y = y > 0.008856 ? Math.pow(y, 1/3) : (7.787 * y) + (16 / 116); + z = z > 0.008856 ? Math.pow(z, 1/3) : (7.787 * z) + (16 / 116); + + l = (116 * y) - 16; + a = 500 * (x - y); + b = 200 * (y - z); + + return [l, a, b]; +} + +function rgb2lch(args) { + return lab2lch(rgb2lab(args)); +} + +function hsl2rgb(hsl) { + var h = hsl[0] / 360, + s = hsl[1] / 100, + l = hsl[2] / 100, + t1, t2, t3, rgb, val; + + if (s == 0) { + val = l * 255; + return [val, val, val]; + } + + if (l < 0.5) + t2 = l * (1 + s); + else + t2 = l + s - l * s; + t1 = 2 * l - t2; + + rgb = [0, 0, 0]; + for (var i = 0; i < 3; i++) { + t3 = h + 1 / 3 * - (i - 1); + t3 < 0 && t3++; + t3 > 1 && t3--; + + if (6 * t3 < 1) + val = t1 + (t2 - t1) * 6 * t3; + else if (2 * t3 < 1) + val = t2; + else if (3 * t3 < 2) + val = t1 + (t2 - t1) * (2 / 3 - t3) * 6; + else + val = t1; + + rgb[i] = val * 255; + } + + return rgb; +} + +function hsl2hsv(hsl) { + var h = hsl[0], + s = hsl[1] / 100, + l = hsl[2] / 100, + sv, v; + + if(l === 0) { + // no need to do calc on black + // also avoids divide by 0 error + return [0, 0, 0]; + } + + l *= 2; + s *= (l <= 1) ? l : 2 - l; + v = (l + s) / 2; + sv = (2 * s) / (l + s); + return [h, sv * 100, v * 100]; +} + +function hsl2hwb(args) { + return rgb2hwb(hsl2rgb(args)); +} + +function hsl2cmyk(args) { + return rgb2cmyk(hsl2rgb(args)); +} + +function hsl2keyword(args) { + return rgb2keyword(hsl2rgb(args)); +} + + +function hsv2rgb(hsv) { + var h = hsv[0] / 60, + s = hsv[1] / 100, + v = hsv[2] / 100, + hi = Math.floor(h) % 6; + + var f = h - Math.floor(h), + p = 255 * v * (1 - s), + q = 255 * v * (1 - (s * f)), + t = 255 * v * (1 - (s * (1 - f))), + v = 255 * v; + + switch(hi) { + case 0: + return [v, t, p]; + case 1: + return [q, v, p]; + case 2: + return [p, v, t]; + case 3: + return [p, q, v]; + case 4: + return [t, p, v]; + case 5: + return [v, p, q]; + } +} + +function hsv2hsl(hsv) { + var h = hsv[0], + s = hsv[1] / 100, + v = hsv[2] / 100, + sl, l; + + l = (2 - s) * v; + sl = s * v; + sl /= (l <= 1) ? l : 2 - l; + sl = sl || 0; + l /= 2; + return [h, sl * 100, l * 100]; +} + +function hsv2hwb(args) { + return rgb2hwb(hsv2rgb(args)) +} + +function hsv2cmyk(args) { + return rgb2cmyk(hsv2rgb(args)); +} + +function hsv2keyword(args) { + return rgb2keyword(hsv2rgb(args)); +} + +// http://dev.w3.org/csswg/css-color/#hwb-to-rgb +function hwb2rgb(hwb) { + var h = hwb[0] / 360, + wh = hwb[1] / 100, + bl = hwb[2] / 100, + ratio = wh + bl, + i, v, f, n; + + // wh + bl cant be > 1 + if (ratio > 1) { + wh /= ratio; + bl /= ratio; + } + + i = Math.floor(6 * h); + v = 1 - bl; + f = 6 * h - i; + if ((i & 0x01) != 0) { + f = 1 - f; + } + n = wh + f * (v - wh); // linear interpolation + + switch (i) { + default: + case 6: + case 0: r = v; g = n; b = wh; break; + case 1: r = n; g = v; b = wh; break; + case 2: r = wh; g = v; b = n; break; + case 3: r = wh; g = n; b = v; break; + case 4: r = n; g = wh; b = v; break; + case 5: r = v; g = wh; b = n; break; + } + + return [r * 255, g * 255, b * 255]; +} + +function hwb2hsl(args) { + return rgb2hsl(hwb2rgb(args)); +} + +function hwb2hsv(args) { + return rgb2hsv(hwb2rgb(args)); +} + +function hwb2cmyk(args) { + return rgb2cmyk(hwb2rgb(args)); +} + +function hwb2keyword(args) { + return rgb2keyword(hwb2rgb(args)); +} + +function cmyk2rgb(cmyk) { + var c = cmyk[0] / 100, + m = cmyk[1] / 100, + y = cmyk[2] / 100, + k = cmyk[3] / 100, + r, g, b; + + r = 1 - Math.min(1, c * (1 - k) + k); + g = 1 - Math.min(1, m * (1 - k) + k); + b = 1 - Math.min(1, y * (1 - k) + k); + return [r * 255, g * 255, b * 255]; +} + +function cmyk2hsl(args) { + return rgb2hsl(cmyk2rgb(args)); +} + +function cmyk2hsv(args) { + return rgb2hsv(cmyk2rgb(args)); +} + +function cmyk2hwb(args) { + return rgb2hwb(cmyk2rgb(args)); +} + +function cmyk2keyword(args) { + return rgb2keyword(cmyk2rgb(args)); +} + + +function xyz2rgb(xyz) { + var x = xyz[0] / 100, + y = xyz[1] / 100, + z = xyz[2] / 100, + r, g, b; + + r = (x * 3.2406) + (y * -1.5372) + (z * -0.4986); + g = (x * -0.9689) + (y * 1.8758) + (z * 0.0415); + b = (x * 0.0557) + (y * -0.2040) + (z * 1.0570); + + // assume sRGB + r = r > 0.0031308 ? ((1.055 * Math.pow(r, 1.0 / 2.4)) - 0.055) + : r = (r * 12.92); + + g = g > 0.0031308 ? ((1.055 * Math.pow(g, 1.0 / 2.4)) - 0.055) + : g = (g * 12.92); + + b = b > 0.0031308 ? ((1.055 * Math.pow(b, 1.0 / 2.4)) - 0.055) + : b = (b * 12.92); + + r = Math.min(Math.max(0, r), 1); + g = Math.min(Math.max(0, g), 1); + b = Math.min(Math.max(0, b), 1); + + return [r * 255, g * 255, b * 255]; +} + +function xyz2lab(xyz) { + var x = xyz[0], + y = xyz[1], + z = xyz[2], + l, a, b; + + x /= 95.047; + y /= 100; + z /= 108.883; + + x = x > 0.008856 ? Math.pow(x, 1/3) : (7.787 * x) + (16 / 116); + y = y > 0.008856 ? Math.pow(y, 1/3) : (7.787 * y) + (16 / 116); + z = z > 0.008856 ? Math.pow(z, 1/3) : (7.787 * z) + (16 / 116); + + l = (116 * y) - 16; + a = 500 * (x - y); + b = 200 * (y - z); + + return [l, a, b]; +} + +function xyz2lch(args) { + return lab2lch(xyz2lab(args)); +} + +function lab2xyz(lab) { + var l = lab[0], + a = lab[1], + b = lab[2], + x, y, z, y2; + + if (l <= 8) { + y = (l * 100) / 903.3; + y2 = (7.787 * (y / 100)) + (16 / 116); + } else { + y = 100 * Math.pow((l + 16) / 116, 3); + y2 = Math.pow(y / 100, 1/3); + } + + x = x / 95.047 <= 0.008856 ? x = (95.047 * ((a / 500) + y2 - (16 / 116))) / 7.787 : 95.047 * Math.pow((a / 500) + y2, 3); + + z = z / 108.883 <= 0.008859 ? z = (108.883 * (y2 - (b / 200) - (16 / 116))) / 7.787 : 108.883 * Math.pow(y2 - (b / 200), 3); + + return [x, y, z]; +} + +function lab2lch(lab) { + var l = lab[0], + a = lab[1], + b = lab[2], + hr, h, c; + + hr = Math.atan2(b, a); + h = hr * 360 / 2 / Math.PI; + if (h < 0) { + h += 360; + } + c = Math.sqrt(a * a + b * b); + return [l, c, h]; +} + +function lab2rgb(args) { + return xyz2rgb(lab2xyz(args)); +} + +function lch2lab(lch) { + var l = lch[0], + c = lch[1], + h = lch[2], + a, b, hr; + + hr = h / 360 * 2 * Math.PI; + a = c * Math.cos(hr); + b = c * Math.sin(hr); + return [l, a, b]; +} + +function lch2xyz(args) { + return lab2xyz(lch2lab(args)); +} + +function lch2rgb(args) { + return lab2rgb(lch2lab(args)); +} + +function keyword2rgb(keyword) { + return cssKeywords[keyword]; +} + +function keyword2hsl(args) { + return rgb2hsl(keyword2rgb(args)); +} + +function keyword2hsv(args) { + return rgb2hsv(keyword2rgb(args)); +} + +function keyword2hwb(args) { + return rgb2hwb(keyword2rgb(args)); +} + +function keyword2cmyk(args) { + return rgb2cmyk(keyword2rgb(args)); +} + +function keyword2lab(args) { + return rgb2lab(keyword2rgb(args)); +} + +function keyword2xyz(args) { + return rgb2xyz(keyword2rgb(args)); +} + +var cssKeywords = { + aliceblue: [240,248,255], + antiquewhite: [250,235,215], + aqua: [0,255,255], + aquamarine: [127,255,212], + azure: [240,255,255], + beige: [245,245,220], + bisque: [255,228,196], + black: [0,0,0], + blanchedalmond: [255,235,205], + blue: [0,0,255], + blueviolet: [138,43,226], + brown: [165,42,42], + burlywood: [222,184,135], + cadetblue: [95,158,160], + chartreuse: [127,255,0], + chocolate: [210,105,30], + coral: [255,127,80], + cornflowerblue: [100,149,237], + cornsilk: [255,248,220], + crimson: [220,20,60], + cyan: [0,255,255], + darkblue: [0,0,139], + darkcyan: [0,139,139], + darkgoldenrod: [184,134,11], + darkgray: [169,169,169], + darkgreen: [0,100,0], + darkgrey: [169,169,169], + darkkhaki: [189,183,107], + darkmagenta: [139,0,139], + darkolivegreen: [85,107,47], + darkorange: [255,140,0], + darkorchid: [153,50,204], + darkred: [139,0,0], + darksalmon: [233,150,122], + darkseagreen: [143,188,143], + darkslateblue: [72,61,139], + darkslategray: [47,79,79], + darkslategrey: [47,79,79], + darkturquoise: [0,206,209], + darkviolet: [148,0,211], + deeppink: [255,20,147], + deepskyblue: [0,191,255], + dimgray: [105,105,105], + dimgrey: [105,105,105], + dodgerblue: [30,144,255], + firebrick: [178,34,34], + floralwhite: [255,250,240], + forestgreen: [34,139,34], + fuchsia: [255,0,255], + gainsboro: [220,220,220], + ghostwhite: [248,248,255], + gold: [255,215,0], + goldenrod: [218,165,32], + gray: [128,128,128], + green: [0,128,0], + greenyellow: [173,255,47], + grey: [128,128,128], + honeydew: [240,255,240], + hotpink: [255,105,180], + indianred: [205,92,92], + indigo: [75,0,130], + ivory: [255,255,240], + khaki: [240,230,140], + lavender: [230,230,250], + lavenderblush: [255,240,245], + lawngreen: [124,252,0], + lemonchiffon: [255,250,205], + lightblue: [173,216,230], + lightcoral: [240,128,128], + lightcyan: [224,255,255], + lightgoldenrodyellow: [250,250,210], + lightgray: [211,211,211], + lightgreen: [144,238,144], + lightgrey: [211,211,211], + lightpink: [255,182,193], + lightsalmon: [255,160,122], + lightseagreen: [32,178,170], + lightskyblue: [135,206,250], + lightslategray: [119,136,153], + lightslategrey: [119,136,153], + lightsteelblue: [176,196,222], + lightyellow: [255,255,224], + lime: [0,255,0], + limegreen: [50,205,50], + linen: [250,240,230], + magenta: [255,0,255], + maroon: [128,0,0], + mediumaquamarine: [102,205,170], + mediumblue: [0,0,205], + mediumorchid: [186,85,211], + mediumpurple: [147,112,219], + mediumseagreen: [60,179,113], + mediumslateblue: [123,104,238], + mediumspringgreen: [0,250,154], + mediumturquoise: [72,209,204], + mediumvioletred: [199,21,133], + midnightblue: [25,25,112], + mintcream: [245,255,250], + mistyrose: [255,228,225], + moccasin: [255,228,181], + navajowhite: [255,222,173], + navy: [0,0,128], + oldlace: [253,245,230], + olive: [128,128,0], + olivedrab: [107,142,35], + orange: [255,165,0], + orangered: [255,69,0], + orchid: [218,112,214], + palegoldenrod: [238,232,170], + palegreen: [152,251,152], + paleturquoise: [175,238,238], + palevioletred: [219,112,147], + papayawhip: [255,239,213], + peachpuff: [255,218,185], + peru: [205,133,63], + pink: [255,192,203], + plum: [221,160,221], + powderblue: [176,224,230], + purple: [128,0,128], + rebeccapurple: [102, 51, 153], + red: [255,0,0], + rosybrown: [188,143,143], + royalblue: [65,105,225], + saddlebrown: [139,69,19], + salmon: [250,128,114], + sandybrown: [244,164,96], + seagreen: [46,139,87], + seashell: [255,245,238], + sienna: [160,82,45], + silver: [192,192,192], + skyblue: [135,206,235], + slateblue: [106,90,205], + slategray: [112,128,144], + slategrey: [112,128,144], + snow: [255,250,250], + springgreen: [0,255,127], + steelblue: [70,130,180], + tan: [210,180,140], + teal: [0,128,128], + thistle: [216,191,216], + tomato: [255,99,71], + turquoise: [64,224,208], + violet: [238,130,238], + wheat: [245,222,179], + white: [255,255,255], + whitesmoke: [245,245,245], + yellow: [255,255,0], + yellowgreen: [154,205,50] +}; + +var reverseKeywords = {}; +for (var key in cssKeywords) { + reverseKeywords[JSON.stringify(cssKeywords[key])] = key; +} + +},{}],5:[function(require,module,exports){ +var conversions = require(4); + +var convert = function() { + return new Converter(); +} + +for (var func in conversions) { + // export Raw versions + convert[func + "Raw"] = (function(func) { + // accept array or plain args + return function(arg) { + if (typeof arg == "number") + arg = Array.prototype.slice.call(arguments); + return conversions[func](arg); + } + })(func); + + var pair = /(\w+)2(\w+)/.exec(func), + from = pair[1], + to = pair[2]; + + // export rgb2hsl and ["rgb"]["hsl"] + convert[from] = convert[from] || {}; + + convert[from][to] = convert[func] = (function(func) { + return function(arg) { + if (typeof arg == "number") + arg = Array.prototype.slice.call(arguments); + + var val = conversions[func](arg); + if (typeof val == "string" || val === undefined) + return val; // keyword + + for (var i = 0; i < val.length; i++) + val[i] = Math.round(val[i]); + return val; + } + })(func); +} + + +/* Converter does lazy conversion and caching */ +var Converter = function() { + this.convs = {}; +}; + +/* Either get the values for a space or + set the values for a space, depending on args */ +Converter.prototype.routeSpace = function(space, args) { + var values = args[0]; + if (values === undefined) { + // color.rgb() + return this.getValues(space); + } + // color.rgb(10, 10, 10) + if (typeof values == "number") { + values = Array.prototype.slice.call(args); + } + + return this.setValues(space, values); +}; + +/* Set the values for a space, invalidating cache */ +Converter.prototype.setValues = function(space, values) { + this.space = space; + this.convs = {}; + this.convs[space] = values; + return this; +}; + +/* Get the values for a space. If there's already + a conversion for the space, fetch it, otherwise + compute it */ +Converter.prototype.getValues = function(space) { + var vals = this.convs[space]; + if (!vals) { + var fspace = this.space, + from = this.convs[fspace]; + vals = convert[fspace][space](from); + + this.convs[space] = vals; + } + return vals; +}; + +["rgb", "hsl", "hsv", "cmyk", "keyword"].forEach(function(space) { + Converter.prototype[space] = function(vals) { + return this.routeSpace(space, arguments); + } +}); + +module.exports = convert; +},{"4":4}],6:[function(require,module,exports){ +'use strict' + +module.exports = { + "aliceblue": [240, 248, 255], + "antiquewhite": [250, 235, 215], + "aqua": [0, 255, 255], + "aquamarine": [127, 255, 212], + "azure": [240, 255, 255], + "beige": [245, 245, 220], + "bisque": [255, 228, 196], + "black": [0, 0, 0], + "blanchedalmond": [255, 235, 205], + "blue": [0, 0, 255], + "blueviolet": [138, 43, 226], + "brown": [165, 42, 42], + "burlywood": [222, 184, 135], + "cadetblue": [95, 158, 160], + "chartreuse": [127, 255, 0], + "chocolate": [210, 105, 30], + "coral": [255, 127, 80], + "cornflowerblue": [100, 149, 237], + "cornsilk": [255, 248, 220], + "crimson": [220, 20, 60], + "cyan": [0, 255, 255], + "darkblue": [0, 0, 139], + "darkcyan": [0, 139, 139], + "darkgoldenrod": [184, 134, 11], + "darkgray": [169, 169, 169], + "darkgreen": [0, 100, 0], + "darkgrey": [169, 169, 169], + "darkkhaki": [189, 183, 107], + "darkmagenta": [139, 0, 139], + "darkolivegreen": [85, 107, 47], + "darkorange": [255, 140, 0], + "darkorchid": [153, 50, 204], + "darkred": [139, 0, 0], + "darksalmon": [233, 150, 122], + "darkseagreen": [143, 188, 143], + "darkslateblue": [72, 61, 139], + "darkslategray": [47, 79, 79], + "darkslategrey": [47, 79, 79], + "darkturquoise": [0, 206, 209], + "darkviolet": [148, 0, 211], + "deeppink": [255, 20, 147], + "deepskyblue": [0, 191, 255], + "dimgray": [105, 105, 105], + "dimgrey": [105, 105, 105], + "dodgerblue": [30, 144, 255], + "firebrick": [178, 34, 34], + "floralwhite": [255, 250, 240], + "forestgreen": [34, 139, 34], + "fuchsia": [255, 0, 255], + "gainsboro": [220, 220, 220], + "ghostwhite": [248, 248, 255], + "gold": [255, 215, 0], + "goldenrod": [218, 165, 32], + "gray": [128, 128, 128], + "green": [0, 128, 0], + "greenyellow": [173, 255, 47], + "grey": [128, 128, 128], + "honeydew": [240, 255, 240], + "hotpink": [255, 105, 180], + "indianred": [205, 92, 92], + "indigo": [75, 0, 130], + "ivory": [255, 255, 240], + "khaki": [240, 230, 140], + "lavender": [230, 230, 250], + "lavenderblush": [255, 240, 245], + "lawngreen": [124, 252, 0], + "lemonchiffon": [255, 250, 205], + "lightblue": [173, 216, 230], + "lightcoral": [240, 128, 128], + "lightcyan": [224, 255, 255], + "lightgoldenrodyellow": [250, 250, 210], + "lightgray": [211, 211, 211], + "lightgreen": [144, 238, 144], + "lightgrey": [211, 211, 211], + "lightpink": [255, 182, 193], + "lightsalmon": [255, 160, 122], + "lightseagreen": [32, 178, 170], + "lightskyblue": [135, 206, 250], + "lightslategray": [119, 136, 153], + "lightslategrey": [119, 136, 153], + "lightsteelblue": [176, 196, 222], + "lightyellow": [255, 255, 224], + "lime": [0, 255, 0], + "limegreen": [50, 205, 50], + "linen": [250, 240, 230], + "magenta": [255, 0, 255], + "maroon": [128, 0, 0], + "mediumaquamarine": [102, 205, 170], + "mediumblue": [0, 0, 205], + "mediumorchid": [186, 85, 211], + "mediumpurple": [147, 112, 219], + "mediumseagreen": [60, 179, 113], + "mediumslateblue": [123, 104, 238], + "mediumspringgreen": [0, 250, 154], + "mediumturquoise": [72, 209, 204], + "mediumvioletred": [199, 21, 133], + "midnightblue": [25, 25, 112], + "mintcream": [245, 255, 250], + "mistyrose": [255, 228, 225], + "moccasin": [255, 228, 181], + "navajowhite": [255, 222, 173], + "navy": [0, 0, 128], + "oldlace": [253, 245, 230], + "olive": [128, 128, 0], + "olivedrab": [107, 142, 35], + "orange": [255, 165, 0], + "orangered": [255, 69, 0], + "orchid": [218, 112, 214], + "palegoldenrod": [238, 232, 170], + "palegreen": [152, 251, 152], + "paleturquoise": [175, 238, 238], + "palevioletred": [219, 112, 147], + "papayawhip": [255, 239, 213], + "peachpuff": [255, 218, 185], + "peru": [205, 133, 63], + "pink": [255, 192, 203], + "plum": [221, 160, 221], + "powderblue": [176, 224, 230], + "purple": [128, 0, 128], + "rebeccapurple": [102, 51, 153], + "red": [255, 0, 0], + "rosybrown": [188, 143, 143], + "royalblue": [65, 105, 225], + "saddlebrown": [139, 69, 19], + "salmon": [250, 128, 114], + "sandybrown": [244, 164, 96], + "seagreen": [46, 139, 87], + "seashell": [255, 245, 238], + "sienna": [160, 82, 45], + "silver": [192, 192, 192], + "skyblue": [135, 206, 235], + "slateblue": [106, 90, 205], + "slategray": [112, 128, 144], + "slategrey": [112, 128, 144], + "snow": [255, 250, 250], + "springgreen": [0, 255, 127], + "steelblue": [70, 130, 180], + "tan": [210, 180, 140], + "teal": [0, 128, 128], + "thistle": [216, 191, 216], + "tomato": [255, 99, 71], + "turquoise": [64, 224, 208], + "violet": [238, 130, 238], + "wheat": [245, 222, 179], + "white": [255, 255, 255], + "whitesmoke": [245, 245, 245], + "yellow": [255, 255, 0], + "yellowgreen": [154, 205, 50] +}; + +},{}],7:[function(require,module,exports){ +/** + * @namespace Chart + */ +var Chart = require(29)(); + +Chart.helpers = require(45); + +// @todo dispatch these helpers into appropriated helpers/helpers.* file and write unit tests! +require(27)(Chart); + +Chart.defaults = require(25); +Chart.Element = require(26); +Chart.elements = require(40); +Chart.Interaction = require(28); +Chart.platform = require(48); + +require(31)(Chart); +require(22)(Chart); +require(23)(Chart); +require(24)(Chart); +require(30)(Chart); +require(33)(Chart); +require(32)(Chart); +require(35)(Chart); + +require(54)(Chart); +require(52)(Chart); +require(53)(Chart); +require(55)(Chart); +require(56)(Chart); +require(57)(Chart); + +// Controllers must be loaded after elements +// See Chart.core.datasetController.dataElementType +require(15)(Chart); +require(16)(Chart); +require(17)(Chart); +require(18)(Chart); +require(19)(Chart); +require(20)(Chart); +require(21)(Chart); + +require(8)(Chart); +require(9)(Chart); +require(10)(Chart); +require(11)(Chart); +require(12)(Chart); +require(13)(Chart); +require(14)(Chart); + +// Loading built-it plugins +var plugins = []; + +plugins.push( + require(49)(Chart), + require(50)(Chart), + require(51)(Chart) +); + +Chart.plugins.register(plugins); + +Chart.platform.initialize(); + +module.exports = Chart; +if (typeof window !== 'undefined') { + window.Chart = Chart; +} + +// DEPRECATIONS + +/** + * Provided for backward compatibility, use Chart.helpers.canvas instead. + * @namespace Chart.canvasHelpers + * @deprecated since version 2.6.0 + * @todo remove at version 3 + * @private + */ +Chart.canvasHelpers = Chart.helpers.canvas; + +},{"10":10,"11":11,"12":12,"13":13,"14":14,"15":15,"16":16,"17":17,"18":18,"19":19,"20":20,"21":21,"22":22,"23":23,"24":24,"25":25,"26":26,"27":27,"28":28,"29":29,"30":30,"31":31,"32":32,"33":33,"35":35,"40":40,"45":45,"48":48,"49":49,"50":50,"51":51,"52":52,"53":53,"54":54,"55":55,"56":56,"57":57,"8":8,"9":9}],8:[function(require,module,exports){ +'use strict'; + +module.exports = function(Chart) { + + Chart.Bar = function(context, config) { + config.type = 'bar'; + + return new Chart(context, config); + }; + +}; + +},{}],9:[function(require,module,exports){ +'use strict'; + +module.exports = function(Chart) { + + Chart.Bubble = function(context, config) { + config.type = 'bubble'; + return new Chart(context, config); + }; + +}; + +},{}],10:[function(require,module,exports){ +'use strict'; + +module.exports = function(Chart) { + + Chart.Doughnut = function(context, config) { + config.type = 'doughnut'; + + return new Chart(context, config); + }; + +}; + +},{}],11:[function(require,module,exports){ +'use strict'; + +module.exports = function(Chart) { + + Chart.Line = function(context, config) { + config.type = 'line'; + + return new Chart(context, config); + }; + +}; + +},{}],12:[function(require,module,exports){ +'use strict'; + +module.exports = function(Chart) { + + Chart.PolarArea = function(context, config) { + config.type = 'polarArea'; + + return new Chart(context, config); + }; + +}; + +},{}],13:[function(require,module,exports){ +'use strict'; + +module.exports = function(Chart) { + + Chart.Radar = function(context, config) { + config.type = 'radar'; + + return new Chart(context, config); + }; + +}; + +},{}],14:[function(require,module,exports){ +'use strict'; + +module.exports = function(Chart) { + Chart.Scatter = function(context, config) { + config.type = 'scatter'; + return new Chart(context, config); + }; +}; + +},{}],15:[function(require,module,exports){ +'use strict'; + +var defaults = require(25); +var elements = require(40); +var helpers = require(45); + +defaults._set('bar', { + hover: { + mode: 'label' + }, + + scales: { + xAxes: [{ + type: 'category', + + // Specific to Bar Controller + categoryPercentage: 0.8, + barPercentage: 0.9, + + // offset settings + offset: true, + + // grid line settings + gridLines: { + offsetGridLines: true + } + }], + + yAxes: [{ + type: 'linear' + }] + } +}); + +defaults._set('horizontalBar', { + hover: { + mode: 'index', + axis: 'y' + }, + + scales: { + xAxes: [{ + type: 'linear', + position: 'bottom' + }], + + yAxes: [{ + position: 'left', + type: 'category', + + // Specific to Horizontal Bar Controller + categoryPercentage: 0.8, + barPercentage: 0.9, + + // offset settings + offset: true, + + // grid line settings + gridLines: { + offsetGridLines: true + } + }] + }, + + elements: { + rectangle: { + borderSkipped: 'left' + } + }, + + tooltips: { + callbacks: { + title: function(item, data) { + // Pick first xLabel for now + var title = ''; + + if (item.length > 0) { + if (item[0].yLabel) { + title = item[0].yLabel; + } else if (data.labels.length > 0 && item[0].index < data.labels.length) { + title = data.labels[item[0].index]; + } + } + + return title; + }, + + label: function(item, data) { + var datasetLabel = data.datasets[item.datasetIndex].label || ''; + return datasetLabel + ': ' + item.xLabel; + } + }, + mode: 'index', + axis: 'y' + } +}); + +module.exports = function(Chart) { + + Chart.controllers.bar = Chart.DatasetController.extend({ + + dataElementType: elements.Rectangle, + + initialize: function() { + var me = this; + var meta; + + Chart.DatasetController.prototype.initialize.apply(me, arguments); + + meta = me.getMeta(); + meta.stack = me.getDataset().stack; + meta.bar = true; + }, + + update: function(reset) { + var me = this; + var rects = me.getMeta().data; + var i, ilen; + + me._ruler = me.getRuler(); + + for (i = 0, ilen = rects.length; i < ilen; ++i) { + me.updateElement(rects[i], i, reset); + } + }, + + updateElement: function(rectangle, index, reset) { + var me = this; + var chart = me.chart; + var meta = me.getMeta(); + var dataset = me.getDataset(); + var custom = rectangle.custom || {}; + var rectangleOptions = chart.options.elements.rectangle; + + rectangle._xScale = me.getScaleForId(meta.xAxisID); + rectangle._yScale = me.getScaleForId(meta.yAxisID); + rectangle._datasetIndex = me.index; + rectangle._index = index; + + rectangle._model = { + datasetLabel: dataset.label, + label: chart.data.labels[index], + borderSkipped: custom.borderSkipped ? custom.borderSkipped : rectangleOptions.borderSkipped, + backgroundColor: custom.backgroundColor ? custom.backgroundColor : helpers.valueAtIndexOrDefault(dataset.backgroundColor, index, rectangleOptions.backgroundColor), + borderColor: custom.borderColor ? custom.borderColor : helpers.valueAtIndexOrDefault(dataset.borderColor, index, rectangleOptions.borderColor), + borderWidth: custom.borderWidth ? custom.borderWidth : helpers.valueAtIndexOrDefault(dataset.borderWidth, index, rectangleOptions.borderWidth) + }; + + me.updateElementGeometry(rectangle, index, reset); + + rectangle.pivot(); + }, + + /** + * @private + */ + updateElementGeometry: function(rectangle, index, reset) { + var me = this; + var model = rectangle._model; + var vscale = me.getValueScale(); + var base = vscale.getBasePixel(); + var horizontal = vscale.isHorizontal(); + var ruler = me._ruler || me.getRuler(); + var vpixels = me.calculateBarValuePixels(me.index, index); + var ipixels = me.calculateBarIndexPixels(me.index, index, ruler); + + model.horizontal = horizontal; + model.base = reset ? base : vpixels.base; + model.x = horizontal ? reset ? base : vpixels.head : ipixels.center; + model.y = horizontal ? ipixels.center : reset ? base : vpixels.head; + model.height = horizontal ? ipixels.size : undefined; + model.width = horizontal ? undefined : ipixels.size; + }, + + /** + * @private + */ + getValueScaleId: function() { + return this.getMeta().yAxisID; + }, + + /** + * @private + */ + getIndexScaleId: function() { + return this.getMeta().xAxisID; + }, + + /** + * @private + */ + getValueScale: function() { + return this.getScaleForId(this.getValueScaleId()); + }, + + /** + * @private + */ + getIndexScale: function() { + return this.getScaleForId(this.getIndexScaleId()); + }, + + /** + * Returns the effective number of stacks based on groups and bar visibility. + * @private + */ + getStackCount: function(last) { + var me = this; + var chart = me.chart; + var scale = me.getIndexScale(); + var stacked = scale.options.stacked; + var ilen = last === undefined ? chart.data.datasets.length : last + 1; + var stacks = []; + var i, meta; + + for (i = 0; i < ilen; ++i) { + meta = chart.getDatasetMeta(i); + if (meta.bar && chart.isDatasetVisible(i) && + (stacked === false || + (stacked === true && stacks.indexOf(meta.stack) === -1) || + (stacked === undefined && (meta.stack === undefined || stacks.indexOf(meta.stack) === -1)))) { + stacks.push(meta.stack); + } + } + + return stacks.length; + }, + + /** + * Returns the stack index for the given dataset based on groups and bar visibility. + * @private + */ + getStackIndex: function(datasetIndex) { + return this.getStackCount(datasetIndex) - 1; + }, + + /** + * @private + */ + getRuler: function() { + var me = this; + var scale = me.getIndexScale(); + var stackCount = me.getStackCount(); + var datasetIndex = me.index; + var pixels = []; + var isHorizontal = scale.isHorizontal(); + var start = isHorizontal ? scale.left : scale.top; + var end = start + (isHorizontal ? scale.width : scale.height); + var i, ilen; + + for (i = 0, ilen = me.getMeta().data.length; i < ilen; ++i) { + pixels.push(scale.getPixelForValue(null, i, datasetIndex)); + } + + return { + pixels: pixels, + start: start, + end: end, + stackCount: stackCount, + scale: scale + }; + }, + + /** + * Note: pixel values are not clamped to the scale area. + * @private + */ + calculateBarValuePixels: function(datasetIndex, index) { + var me = this; + var chart = me.chart; + var meta = me.getMeta(); + var scale = me.getValueScale(); + var datasets = chart.data.datasets; + var value = scale.getRightValue(datasets[datasetIndex].data[index]); + var stacked = scale.options.stacked; + var stack = meta.stack; + var start = 0; + var i, imeta, ivalue, base, head, size; + + if (stacked || (stacked === undefined && stack !== undefined)) { + for (i = 0; i < datasetIndex; ++i) { + imeta = chart.getDatasetMeta(i); + + if (imeta.bar && + imeta.stack === stack && + imeta.controller.getValueScaleId() === scale.id && + chart.isDatasetVisible(i)) { + + ivalue = scale.getRightValue(datasets[i].data[index]); + if ((value < 0 && ivalue < 0) || (value >= 0 && ivalue > 0)) { + start += ivalue; + } + } + } + } + + base = scale.getPixelForValue(start); + head = scale.getPixelForValue(start + value); + size = (head - base) / 2; + + return { + size: size, + base: base, + head: head, + center: head + size / 2 + }; + }, + + /** + * @private + */ + calculateBarIndexPixels: function(datasetIndex, index, ruler) { + var me = this; + var options = ruler.scale.options; + var stackIndex = me.getStackIndex(datasetIndex); + var pixels = ruler.pixels; + var base = pixels[index]; + var length = pixels.length; + var start = ruler.start; + var end = ruler.end; + var leftSampleSize, rightSampleSize, leftCategorySize, rightCategorySize, fullBarSize, size; + + if (length === 1) { + leftSampleSize = base > start ? base - start : end - base; + rightSampleSize = base < end ? end - base : base - start; + } else { + if (index > 0) { + leftSampleSize = (base - pixels[index - 1]) / 2; + if (index === length - 1) { + rightSampleSize = leftSampleSize; + } + } + if (index < length - 1) { + rightSampleSize = (pixels[index + 1] - base) / 2; + if (index === 0) { + leftSampleSize = rightSampleSize; + } + } + } + + leftCategorySize = leftSampleSize * options.categoryPercentage; + rightCategorySize = rightSampleSize * options.categoryPercentage; + fullBarSize = (leftCategorySize + rightCategorySize) / ruler.stackCount; + size = fullBarSize * options.barPercentage; + + size = Math.min( + helpers.valueOrDefault(options.barThickness, size), + helpers.valueOrDefault(options.maxBarThickness, Infinity)); + + base -= leftCategorySize; + base += fullBarSize * stackIndex; + base += (fullBarSize - size) / 2; + + return { + size: size, + base: base, + head: base + size, + center: base + size / 2 + }; + }, + + draw: function() { + var me = this; + var chart = me.chart; + var scale = me.getValueScale(); + var rects = me.getMeta().data; + var dataset = me.getDataset(); + var ilen = rects.length; + var i = 0; + + helpers.canvas.clipArea(chart.ctx, chart.chartArea); + + for (; i < ilen; ++i) { + if (!isNaN(scale.getRightValue(dataset.data[i]))) { + rects[i].draw(); + } + } + + helpers.canvas.unclipArea(chart.ctx); + }, + + setHoverStyle: function(rectangle) { + var dataset = this.chart.data.datasets[rectangle._datasetIndex]; + var index = rectangle._index; + var custom = rectangle.custom || {}; + var model = rectangle._model; + + model.backgroundColor = custom.hoverBackgroundColor ? custom.hoverBackgroundColor : helpers.valueAtIndexOrDefault(dataset.hoverBackgroundColor, index, helpers.getHoverColor(model.backgroundColor)); + model.borderColor = custom.hoverBorderColor ? custom.hoverBorderColor : helpers.valueAtIndexOrDefault(dataset.hoverBorderColor, index, helpers.getHoverColor(model.borderColor)); + model.borderWidth = custom.hoverBorderWidth ? custom.hoverBorderWidth : helpers.valueAtIndexOrDefault(dataset.hoverBorderWidth, index, model.borderWidth); + }, + + removeHoverStyle: function(rectangle) { + var dataset = this.chart.data.datasets[rectangle._datasetIndex]; + var index = rectangle._index; + var custom = rectangle.custom || {}; + var model = rectangle._model; + var rectangleElementOptions = this.chart.options.elements.rectangle; + + model.backgroundColor = custom.backgroundColor ? custom.backgroundColor : helpers.valueAtIndexOrDefault(dataset.backgroundColor, index, rectangleElementOptions.backgroundColor); + model.borderColor = custom.borderColor ? custom.borderColor : helpers.valueAtIndexOrDefault(dataset.borderColor, index, rectangleElementOptions.borderColor); + model.borderWidth = custom.borderWidth ? custom.borderWidth : helpers.valueAtIndexOrDefault(dataset.borderWidth, index, rectangleElementOptions.borderWidth); + } + }); + + Chart.controllers.horizontalBar = Chart.controllers.bar.extend({ + /** + * @private + */ + getValueScaleId: function() { + return this.getMeta().xAxisID; + }, + + /** + * @private + */ + getIndexScaleId: function() { + return this.getMeta().yAxisID; + } + }); +}; + +},{"25":25,"40":40,"45":45}],16:[function(require,module,exports){ +'use strict'; + +var defaults = require(25); +var elements = require(40); +var helpers = require(45); + +defaults._set('bubble', { + hover: { + mode: 'single' + }, + + scales: { + xAxes: [{ + type: 'linear', // bubble should probably use a linear scale by default + position: 'bottom', + id: 'x-axis-0' // need an ID so datasets can reference the scale + }], + yAxes: [{ + type: 'linear', + position: 'left', + id: 'y-axis-0' + }] + }, + + tooltips: { + callbacks: { + title: function() { + // Title doesn't make sense for scatter since we format the data as a point + return ''; + }, + label: function(item, data) { + var datasetLabel = data.datasets[item.datasetIndex].label || ''; + var dataPoint = data.datasets[item.datasetIndex].data[item.index]; + return datasetLabel + ': (' + item.xLabel + ', ' + item.yLabel + ', ' + dataPoint.r + ')'; + } + } + } +}); + + +module.exports = function(Chart) { + + Chart.controllers.bubble = Chart.DatasetController.extend({ + /** + * @protected + */ + dataElementType: elements.Point, + + /** + * @protected + */ + update: function(reset) { + var me = this; + var meta = me.getMeta(); + var points = meta.data; + + // Update Points + helpers.each(points, function(point, index) { + me.updateElement(point, index, reset); + }); + }, + + /** + * @protected + */ + updateElement: function(point, index, reset) { + var me = this; + var meta = me.getMeta(); + var custom = point.custom || {}; + var xScale = me.getScaleForId(meta.xAxisID); + var yScale = me.getScaleForId(meta.yAxisID); + var options = me._resolveElementOptions(point, index); + var data = me.getDataset().data[index]; + var dsIndex = me.index; + + var x = reset ? xScale.getPixelForDecimal(0.5) : xScale.getPixelForValue(typeof data === 'object' ? data : NaN, index, dsIndex); + var y = reset ? yScale.getBasePixel() : yScale.getPixelForValue(data, index, dsIndex); + + point._xScale = xScale; + point._yScale = yScale; + point._options = options; + point._datasetIndex = dsIndex; + point._index = index; + point._model = { + backgroundColor: options.backgroundColor, + borderColor: options.borderColor, + borderWidth: options.borderWidth, + hitRadius: options.hitRadius, + pointStyle: options.pointStyle, + radius: reset ? 0 : options.radius, + skip: custom.skip || isNaN(x) || isNaN(y), + x: x, + y: y, + }; + + point.pivot(); + }, + + /** + * @protected + */ + setHoverStyle: function(point) { + var model = point._model; + var options = point._options; + + model.backgroundColor = helpers.valueOrDefault(options.hoverBackgroundColor, helpers.getHoverColor(options.backgroundColor)); + model.borderColor = helpers.valueOrDefault(options.hoverBorderColor, helpers.getHoverColor(options.borderColor)); + model.borderWidth = helpers.valueOrDefault(options.hoverBorderWidth, options.borderWidth); + model.radius = options.radius + options.hoverRadius; + }, + + /** + * @protected + */ + removeHoverStyle: function(point) { + var model = point._model; + var options = point._options; + + model.backgroundColor = options.backgroundColor; + model.borderColor = options.borderColor; + model.borderWidth = options.borderWidth; + model.radius = options.radius; + }, + + /** + * @private + */ + _resolveElementOptions: function(point, index) { + var me = this; + var chart = me.chart; + var datasets = chart.data.datasets; + var dataset = datasets[me.index]; + var custom = point.custom || {}; + var options = chart.options.elements.point; + var resolve = helpers.options.resolve; + var data = dataset.data[index]; + var values = {}; + var i, ilen, key; + + // Scriptable options + var context = { + chart: chart, + dataIndex: index, + dataset: dataset, + datasetIndex: me.index + }; + + var keys = [ + 'backgroundColor', + 'borderColor', + 'borderWidth', + 'hoverBackgroundColor', + 'hoverBorderColor', + 'hoverBorderWidth', + 'hoverRadius', + 'hitRadius', + 'pointStyle' + ]; + + for (i = 0, ilen = keys.length; i < ilen; ++i) { + key = keys[i]; + values[key] = resolve([ + custom[key], + dataset[key], + options[key] + ], context, index); + } + + // Custom radius resolution + values.radius = resolve([ + custom.radius, + data ? data.r : undefined, + dataset.radius, + options.radius + ], context, index); + + return values; + } + }); +}; + +},{"25":25,"40":40,"45":45}],17:[function(require,module,exports){ +'use strict'; + +var defaults = require(25); +var elements = require(40); +var helpers = require(45); + +defaults._set('doughnut', { + animation: { + // Boolean - Whether we animate the rotation of the Doughnut + animateRotate: true, + // Boolean - Whether we animate scaling the Doughnut from the centre + animateScale: false + }, + hover: { + mode: 'single' + }, + legendCallback: function(chart) { + var text = []; + text.push(''); + return text.join(''); + }, + legend: { + labels: { + generateLabels: function(chart) { + var data = chart.data; + if (data.labels.length && data.datasets.length) { + return data.labels.map(function(label, i) { + var meta = chart.getDatasetMeta(0); + var ds = data.datasets[0]; + var arc = meta.data[i]; + var custom = arc && arc.custom || {}; + var valueAtIndexOrDefault = helpers.valueAtIndexOrDefault; + var arcOpts = chart.options.elements.arc; + var fill = custom.backgroundColor ? custom.backgroundColor : valueAtIndexOrDefault(ds.backgroundColor, i, arcOpts.backgroundColor); + var stroke = custom.borderColor ? custom.borderColor : valueAtIndexOrDefault(ds.borderColor, i, arcOpts.borderColor); + var bw = custom.borderWidth ? custom.borderWidth : valueAtIndexOrDefault(ds.borderWidth, i, arcOpts.borderWidth); + + return { + text: label, + fillStyle: fill, + strokeStyle: stroke, + lineWidth: bw, + hidden: isNaN(ds.data[i]) || meta.data[i].hidden, + + // Extra data used for toggling the correct item + index: i + }; + }); + } + return []; + } + }, + + onClick: function(e, legendItem) { + var index = legendItem.index; + var chart = this.chart; + var i, ilen, meta; + + for (i = 0, ilen = (chart.data.datasets || []).length; i < ilen; ++i) { + meta = chart.getDatasetMeta(i); + // toggle visibility of index if exists + if (meta.data[index]) { + meta.data[index].hidden = !meta.data[index].hidden; + } + } + + chart.update(); + } + }, + + // The percentage of the chart that we cut out of the middle. + cutoutPercentage: 50, + + // The rotation of the chart, where the first data arc begins. + rotation: Math.PI * -0.5, + + // The total circumference of the chart. + circumference: Math.PI * 2.0, + + // Need to override these to give a nice default + tooltips: { + callbacks: { + title: function() { + return ''; + }, + label: function(tooltipItem, data) { + var dataLabel = data.labels[tooltipItem.index]; + var value = ': ' + data.datasets[tooltipItem.datasetIndex].data[tooltipItem.index]; + + if (helpers.isArray(dataLabel)) { + // show value on first line of multiline label + // need to clone because we are changing the value + dataLabel = dataLabel.slice(); + dataLabel[0] += value; + } else { + dataLabel += value; + } + + return dataLabel; + } + } + } +}); + +defaults._set('pie', helpers.clone(defaults.doughnut)); +defaults._set('pie', { + cutoutPercentage: 0 +}); + +module.exports = function(Chart) { + + Chart.controllers.doughnut = Chart.controllers.pie = Chart.DatasetController.extend({ + + dataElementType: elements.Arc, + + linkScales: helpers.noop, + + // Get index of the dataset in relation to the visible datasets. This allows determining the inner and outer radius correctly + getRingIndex: function(datasetIndex) { + var ringIndex = 0; + + for (var j = 0; j < datasetIndex; ++j) { + if (this.chart.isDatasetVisible(j)) { + ++ringIndex; + } + } + + return ringIndex; + }, + + update: function(reset) { + var me = this; + var chart = me.chart; + var chartArea = chart.chartArea; + var opts = chart.options; + var arcOpts = opts.elements.arc; + var availableWidth = chartArea.right - chartArea.left - arcOpts.borderWidth; + var availableHeight = chartArea.bottom - chartArea.top - arcOpts.borderWidth; + var minSize = Math.min(availableWidth, availableHeight); + var offset = {x: 0, y: 0}; + var meta = me.getMeta(); + var cutoutPercentage = opts.cutoutPercentage; + var circumference = opts.circumference; + + // If the chart's circumference isn't a full circle, calculate minSize as a ratio of the width/height of the arc + if (circumference < Math.PI * 2.0) { + var startAngle = opts.rotation % (Math.PI * 2.0); + startAngle += Math.PI * 2.0 * (startAngle >= Math.PI ? -1 : startAngle < -Math.PI ? 1 : 0); + var endAngle = startAngle + circumference; + var start = {x: Math.cos(startAngle), y: Math.sin(startAngle)}; + var end = {x: Math.cos(endAngle), y: Math.sin(endAngle)}; + var contains0 = (startAngle <= 0 && endAngle >= 0) || (startAngle <= Math.PI * 2.0 && Math.PI * 2.0 <= endAngle); + var contains90 = (startAngle <= Math.PI * 0.5 && Math.PI * 0.5 <= endAngle) || (startAngle <= Math.PI * 2.5 && Math.PI * 2.5 <= endAngle); + var contains180 = (startAngle <= -Math.PI && -Math.PI <= endAngle) || (startAngle <= Math.PI && Math.PI <= endAngle); + var contains270 = (startAngle <= -Math.PI * 0.5 && -Math.PI * 0.5 <= endAngle) || (startAngle <= Math.PI * 1.5 && Math.PI * 1.5 <= endAngle); + var cutout = cutoutPercentage / 100.0; + var min = {x: contains180 ? -1 : Math.min(start.x * (start.x < 0 ? 1 : cutout), end.x * (end.x < 0 ? 1 : cutout)), y: contains270 ? -1 : Math.min(start.y * (start.y < 0 ? 1 : cutout), end.y * (end.y < 0 ? 1 : cutout))}; + var max = {x: contains0 ? 1 : Math.max(start.x * (start.x > 0 ? 1 : cutout), end.x * (end.x > 0 ? 1 : cutout)), y: contains90 ? 1 : Math.max(start.y * (start.y > 0 ? 1 : cutout), end.y * (end.y > 0 ? 1 : cutout))}; + var size = {width: (max.x - min.x) * 0.5, height: (max.y - min.y) * 0.5}; + minSize = Math.min(availableWidth / size.width, availableHeight / size.height); + offset = {x: (max.x + min.x) * -0.5, y: (max.y + min.y) * -0.5}; + } + + chart.borderWidth = me.getMaxBorderWidth(meta.data); + chart.outerRadius = Math.max((minSize - chart.borderWidth) / 2, 0); + chart.innerRadius = Math.max(cutoutPercentage ? (chart.outerRadius / 100) * (cutoutPercentage) : 0, 0); + chart.radiusLength = (chart.outerRadius - chart.innerRadius) / chart.getVisibleDatasetCount(); + chart.offsetX = offset.x * chart.outerRadius; + chart.offsetY = offset.y * chart.outerRadius; + + meta.total = me.calculateTotal(); + + me.outerRadius = chart.outerRadius - (chart.radiusLength * me.getRingIndex(me.index)); + me.innerRadius = Math.max(me.outerRadius - chart.radiusLength, 0); + + helpers.each(meta.data, function(arc, index) { + me.updateElement(arc, index, reset); + }); + }, + + updateElement: function(arc, index, reset) { + var me = this; + var chart = me.chart; + var chartArea = chart.chartArea; + var opts = chart.options; + var animationOpts = opts.animation; + var centerX = (chartArea.left + chartArea.right) / 2; + var centerY = (chartArea.top + chartArea.bottom) / 2; + var startAngle = opts.rotation; // non reset case handled later + var endAngle = opts.rotation; // non reset case handled later + var dataset = me.getDataset(); + var circumference = reset && animationOpts.animateRotate ? 0 : arc.hidden ? 0 : me.calculateCircumference(dataset.data[index]) * (opts.circumference / (2.0 * Math.PI)); + var innerRadius = reset && animationOpts.animateScale ? 0 : me.innerRadius; + var outerRadius = reset && animationOpts.animateScale ? 0 : me.outerRadius; + var valueAtIndexOrDefault = helpers.valueAtIndexOrDefault; + + helpers.extend(arc, { + // Utility + _datasetIndex: me.index, + _index: index, + + // Desired view properties + _model: { + x: centerX + chart.offsetX, + y: centerY + chart.offsetY, + startAngle: startAngle, + endAngle: endAngle, + circumference: circumference, + outerRadius: outerRadius, + innerRadius: innerRadius, + label: valueAtIndexOrDefault(dataset.label, index, chart.data.labels[index]) + } + }); + + var model = arc._model; + // Resets the visual styles + this.removeHoverStyle(arc); + + // Set correct angles if not resetting + if (!reset || !animationOpts.animateRotate) { + if (index === 0) { + model.startAngle = opts.rotation; + } else { + model.startAngle = me.getMeta().data[index - 1]._model.endAngle; + } + + model.endAngle = model.startAngle + model.circumference; + } + + arc.pivot(); + }, + + removeHoverStyle: function(arc) { + Chart.DatasetController.prototype.removeHoverStyle.call(this, arc, this.chart.options.elements.arc); + }, + + calculateTotal: function() { + var dataset = this.getDataset(); + var meta = this.getMeta(); + var total = 0; + var value; + + helpers.each(meta.data, function(element, index) { + value = dataset.data[index]; + if (!isNaN(value) && !element.hidden) { + total += Math.abs(value); + } + }); + + /* if (total === 0) { + total = NaN; + }*/ + + return total; + }, + + calculateCircumference: function(value) { + var total = this.getMeta().total; + if (total > 0 && !isNaN(value)) { + return (Math.PI * 2.0) * (value / total); + } + return 0; + }, + + // gets the max border or hover width to properly scale pie charts + getMaxBorderWidth: function(arcs) { + var max = 0; + var index = this.index; + var length = arcs.length; + var borderWidth; + var hoverWidth; + + for (var i = 0; i < length; i++) { + borderWidth = arcs[i]._model ? arcs[i]._model.borderWidth : 0; + hoverWidth = arcs[i]._chart ? arcs[i]._chart.config.data.datasets[index].hoverBorderWidth : 0; + + max = borderWidth > max ? borderWidth : max; + max = hoverWidth > max ? hoverWidth : max; + } + return max; + } + }); +}; + +},{"25":25,"40":40,"45":45}],18:[function(require,module,exports){ +'use strict'; + +var defaults = require(25); +var elements = require(40); +var helpers = require(45); + +defaults._set('line', { + showLines: true, + spanGaps: false, + + hover: { + mode: 'label' + }, + + scales: { + xAxes: [{ + type: 'category', + id: 'x-axis-0' + }], + yAxes: [{ + type: 'linear', + id: 'y-axis-0' + }] + } +}); + +module.exports = function(Chart) { + + function lineEnabled(dataset, options) { + return helpers.valueOrDefault(dataset.showLine, options.showLines); + } + + Chart.controllers.line = Chart.DatasetController.extend({ + + datasetElementType: elements.Line, + + dataElementType: elements.Point, + + update: function(reset) { + var me = this; + var meta = me.getMeta(); + var line = meta.dataset; + var points = meta.data || []; + var options = me.chart.options; + var lineElementOptions = options.elements.line; + var scale = me.getScaleForId(meta.yAxisID); + var i, ilen, custom; + var dataset = me.getDataset(); + var showLine = lineEnabled(dataset, options); + + // Update Line + if (showLine) { + custom = line.custom || {}; + + // Compatibility: If the properties are defined with only the old name, use those values + if ((dataset.tension !== undefined) && (dataset.lineTension === undefined)) { + dataset.lineTension = dataset.tension; + } + + // Utility + line._scale = scale; + line._datasetIndex = me.index; + // Data + line._children = points; + // Model + line._model = { + // Appearance + // The default behavior of lines is to break at null values, according + // to https://github.com/chartjs/Chart.js/issues/2435#issuecomment-216718158 + // This option gives lines the ability to span gaps + spanGaps: dataset.spanGaps ? dataset.spanGaps : options.spanGaps, + tension: custom.tension ? custom.tension : helpers.valueOrDefault(dataset.lineTension, lineElementOptions.tension), + backgroundColor: custom.backgroundColor ? custom.backgroundColor : (dataset.backgroundColor || lineElementOptions.backgroundColor), + borderWidth: custom.borderWidth ? custom.borderWidth : (dataset.borderWidth || lineElementOptions.borderWidth), + borderColor: custom.borderColor ? custom.borderColor : (dataset.borderColor || lineElementOptions.borderColor), + borderCapStyle: custom.borderCapStyle ? custom.borderCapStyle : (dataset.borderCapStyle || lineElementOptions.borderCapStyle), + borderDash: custom.borderDash ? custom.borderDash : (dataset.borderDash || lineElementOptions.borderDash), + borderDashOffset: custom.borderDashOffset ? custom.borderDashOffset : (dataset.borderDashOffset || lineElementOptions.borderDashOffset), + borderJoinStyle: custom.borderJoinStyle ? custom.borderJoinStyle : (dataset.borderJoinStyle || lineElementOptions.borderJoinStyle), + fill: custom.fill ? custom.fill : (dataset.fill !== undefined ? dataset.fill : lineElementOptions.fill), + steppedLine: custom.steppedLine ? custom.steppedLine : helpers.valueOrDefault(dataset.steppedLine, lineElementOptions.stepped), + cubicInterpolationMode: custom.cubicInterpolationMode ? custom.cubicInterpolationMode : helpers.valueOrDefault(dataset.cubicInterpolationMode, lineElementOptions.cubicInterpolationMode), + }; + + line.pivot(); + } + + // Update Points + for (i = 0, ilen = points.length; i < ilen; ++i) { + me.updateElement(points[i], i, reset); + } + + if (showLine && line._model.tension !== 0) { + me.updateBezierControlPoints(); + } + + // Now pivot the point for animation + for (i = 0, ilen = points.length; i < ilen; ++i) { + points[i].pivot(); + } + }, + + getPointBackgroundColor: function(point, index) { + var backgroundColor = this.chart.options.elements.point.backgroundColor; + var dataset = this.getDataset(); + var custom = point.custom || {}; + + if (custom.backgroundColor) { + backgroundColor = custom.backgroundColor; + } else if (dataset.pointBackgroundColor) { + backgroundColor = helpers.valueAtIndexOrDefault(dataset.pointBackgroundColor, index, backgroundColor); + } else if (dataset.backgroundColor) { + backgroundColor = dataset.backgroundColor; + } + + return backgroundColor; + }, + + getPointBorderColor: function(point, index) { + var borderColor = this.chart.options.elements.point.borderColor; + var dataset = this.getDataset(); + var custom = point.custom || {}; + + if (custom.borderColor) { + borderColor = custom.borderColor; + } else if (dataset.pointBorderColor) { + borderColor = helpers.valueAtIndexOrDefault(dataset.pointBorderColor, index, borderColor); + } else if (dataset.borderColor) { + borderColor = dataset.borderColor; + } + + return borderColor; + }, + + getPointBorderWidth: function(point, index) { + var borderWidth = this.chart.options.elements.point.borderWidth; + var dataset = this.getDataset(); + var custom = point.custom || {}; + + if (!isNaN(custom.borderWidth)) { + borderWidth = custom.borderWidth; + } else if (!isNaN(dataset.pointBorderWidth) || helpers.isArray(dataset.pointBorderWidth)) { + borderWidth = helpers.valueAtIndexOrDefault(dataset.pointBorderWidth, index, borderWidth); + } else if (!isNaN(dataset.borderWidth)) { + borderWidth = dataset.borderWidth; + } + + return borderWidth; + }, + + updateElement: function(point, index, reset) { + var me = this; + var meta = me.getMeta(); + var custom = point.custom || {}; + var dataset = me.getDataset(); + var datasetIndex = me.index; + var value = dataset.data[index]; + var yScale = me.getScaleForId(meta.yAxisID); + var xScale = me.getScaleForId(meta.xAxisID); + var pointOptions = me.chart.options.elements.point; + var x, y; + + // Compatibility: If the properties are defined with only the old name, use those values + if ((dataset.radius !== undefined) && (dataset.pointRadius === undefined)) { + dataset.pointRadius = dataset.radius; + } + if ((dataset.hitRadius !== undefined) && (dataset.pointHitRadius === undefined)) { + dataset.pointHitRadius = dataset.hitRadius; + } + + x = xScale.getPixelForValue(typeof value === 'object' ? value : NaN, index, datasetIndex); + y = reset ? yScale.getBasePixel() : me.calculatePointY(value, index, datasetIndex); + + // Utility + point._xScale = xScale; + point._yScale = yScale; + point._datasetIndex = datasetIndex; + point._index = index; + + // Desired view properties + point._model = { + x: x, + y: y, + skip: custom.skip || isNaN(x) || isNaN(y), + // Appearance + radius: custom.radius || helpers.valueAtIndexOrDefault(dataset.pointRadius, index, pointOptions.radius), + pointStyle: custom.pointStyle || helpers.valueAtIndexOrDefault(dataset.pointStyle, index, pointOptions.pointStyle), + backgroundColor: me.getPointBackgroundColor(point, index), + borderColor: me.getPointBorderColor(point, index), + borderWidth: me.getPointBorderWidth(point, index), + tension: meta.dataset._model ? meta.dataset._model.tension : 0, + steppedLine: meta.dataset._model ? meta.dataset._model.steppedLine : false, + // Tooltip + hitRadius: custom.hitRadius || helpers.valueAtIndexOrDefault(dataset.pointHitRadius, index, pointOptions.hitRadius) + }; + }, + + calculatePointY: function(value, index, datasetIndex) { + var me = this; + var chart = me.chart; + var meta = me.getMeta(); + var yScale = me.getScaleForId(meta.yAxisID); + var sumPos = 0; + var sumNeg = 0; + var i, ds, dsMeta; + + if (yScale.options.stacked) { + for (i = 0; i < datasetIndex; i++) { + ds = chart.data.datasets[i]; + dsMeta = chart.getDatasetMeta(i); + if (dsMeta.type === 'line' && dsMeta.yAxisID === yScale.id && chart.isDatasetVisible(i)) { + var stackedRightValue = Number(yScale.getRightValue(ds.data[index])); + if (stackedRightValue < 0) { + sumNeg += stackedRightValue || 0; + } else { + sumPos += stackedRightValue || 0; + } + } + } + + var rightValue = Number(yScale.getRightValue(value)); + if (rightValue < 0) { + return yScale.getPixelForValue(sumNeg + rightValue); + } + return yScale.getPixelForValue(sumPos + rightValue); + } + + return yScale.getPixelForValue(value); + }, + + updateBezierControlPoints: function() { + var me = this; + var meta = me.getMeta(); + var area = me.chart.chartArea; + var points = (meta.data || []); + var i, ilen, point, model, controlPoints; + + // Only consider points that are drawn in case the spanGaps option is used + if (meta.dataset._model.spanGaps) { + points = points.filter(function(pt) { + return !pt._model.skip; + }); + } + + function capControlPoint(pt, min, max) { + return Math.max(Math.min(pt, max), min); + } + + if (meta.dataset._model.cubicInterpolationMode === 'monotone') { + helpers.splineCurveMonotone(points); + } else { + for (i = 0, ilen = points.length; i < ilen; ++i) { + point = points[i]; + model = point._model; + controlPoints = helpers.splineCurve( + helpers.previousItem(points, i)._model, + model, + helpers.nextItem(points, i)._model, + meta.dataset._model.tension + ); + model.controlPointPreviousX = controlPoints.previous.x; + model.controlPointPreviousY = controlPoints.previous.y; + model.controlPointNextX = controlPoints.next.x; + model.controlPointNextY = controlPoints.next.y; + } + } + + if (me.chart.options.elements.line.capBezierPoints) { + for (i = 0, ilen = points.length; i < ilen; ++i) { + model = points[i]._model; + model.controlPointPreviousX = capControlPoint(model.controlPointPreviousX, area.left, area.right); + model.controlPointPreviousY = capControlPoint(model.controlPointPreviousY, area.top, area.bottom); + model.controlPointNextX = capControlPoint(model.controlPointNextX, area.left, area.right); + model.controlPointNextY = capControlPoint(model.controlPointNextY, area.top, area.bottom); + } + } + }, + + draw: function() { + var me = this; + var chart = me.chart; + var meta = me.getMeta(); + var points = meta.data || []; + var area = chart.chartArea; + var ilen = points.length; + var i = 0; + + helpers.canvas.clipArea(chart.ctx, area); + + if (lineEnabled(me.getDataset(), chart.options)) { + meta.dataset.draw(); + } + + helpers.canvas.unclipArea(chart.ctx); + + // Draw the points + for (; i < ilen; ++i) { + points[i].draw(area); + } + }, + + setHoverStyle: function(point) { + // Point + var dataset = this.chart.data.datasets[point._datasetIndex]; + var index = point._index; + var custom = point.custom || {}; + var model = point._model; + + model.radius = custom.hoverRadius || helpers.valueAtIndexOrDefault(dataset.pointHoverRadius, index, this.chart.options.elements.point.hoverRadius); + model.backgroundColor = custom.hoverBackgroundColor || helpers.valueAtIndexOrDefault(dataset.pointHoverBackgroundColor, index, helpers.getHoverColor(model.backgroundColor)); + model.borderColor = custom.hoverBorderColor || helpers.valueAtIndexOrDefault(dataset.pointHoverBorderColor, index, helpers.getHoverColor(model.borderColor)); + model.borderWidth = custom.hoverBorderWidth || helpers.valueAtIndexOrDefault(dataset.pointHoverBorderWidth, index, model.borderWidth); + }, + + removeHoverStyle: function(point) { + var me = this; + var dataset = me.chart.data.datasets[point._datasetIndex]; + var index = point._index; + var custom = point.custom || {}; + var model = point._model; + + // Compatibility: If the properties are defined with only the old name, use those values + if ((dataset.radius !== undefined) && (dataset.pointRadius === undefined)) { + dataset.pointRadius = dataset.radius; + } + + model.radius = custom.radius || helpers.valueAtIndexOrDefault(dataset.pointRadius, index, me.chart.options.elements.point.radius); + model.backgroundColor = me.getPointBackgroundColor(point, index); + model.borderColor = me.getPointBorderColor(point, index); + model.borderWidth = me.getPointBorderWidth(point, index); + } + }); +}; + +},{"25":25,"40":40,"45":45}],19:[function(require,module,exports){ +'use strict'; + +var defaults = require(25); +var elements = require(40); +var helpers = require(45); + +defaults._set('polarArea', { + scale: { + type: 'radialLinear', + angleLines: { + display: false + }, + gridLines: { + circular: true + }, + pointLabels: { + display: false + }, + ticks: { + beginAtZero: true + } + }, + + // Boolean - Whether to animate the rotation of the chart + animation: { + animateRotate: true, + animateScale: true + }, + + startAngle: -0.5 * Math.PI, + legendCallback: function(chart) { + var text = []; + text.push(''); + return text.join(''); + }, + legend: { + labels: { + generateLabels: function(chart) { + var data = chart.data; + if (data.labels.length && data.datasets.length) { + return data.labels.map(function(label, i) { + var meta = chart.getDatasetMeta(0); + var ds = data.datasets[0]; + var arc = meta.data[i]; + var custom = arc.custom || {}; + var valueAtIndexOrDefault = helpers.valueAtIndexOrDefault; + var arcOpts = chart.options.elements.arc; + var fill = custom.backgroundColor ? custom.backgroundColor : valueAtIndexOrDefault(ds.backgroundColor, i, arcOpts.backgroundColor); + var stroke = custom.borderColor ? custom.borderColor : valueAtIndexOrDefault(ds.borderColor, i, arcOpts.borderColor); + var bw = custom.borderWidth ? custom.borderWidth : valueAtIndexOrDefault(ds.borderWidth, i, arcOpts.borderWidth); + + return { + text: label, + fillStyle: fill, + strokeStyle: stroke, + lineWidth: bw, + hidden: isNaN(ds.data[i]) || meta.data[i].hidden, + + // Extra data used for toggling the correct item + index: i + }; + }); + } + return []; + } + }, + + onClick: function(e, legendItem) { + var index = legendItem.index; + var chart = this.chart; + var i, ilen, meta; + + for (i = 0, ilen = (chart.data.datasets || []).length; i < ilen; ++i) { + meta = chart.getDatasetMeta(i); + meta.data[index].hidden = !meta.data[index].hidden; + } + + chart.update(); + } + }, + + // Need to override these to give a nice default + tooltips: { + callbacks: { + title: function() { + return ''; + }, + label: function(item, data) { + return data.labels[item.index] + ': ' + item.yLabel; + } + } + } +}); + +module.exports = function(Chart) { + + Chart.controllers.polarArea = Chart.DatasetController.extend({ + + dataElementType: elements.Arc, + + linkScales: helpers.noop, + + update: function(reset) { + var me = this; + var chart = me.chart; + var chartArea = chart.chartArea; + var meta = me.getMeta(); + var opts = chart.options; + var arcOpts = opts.elements.arc; + var minSize = Math.min(chartArea.right - chartArea.left, chartArea.bottom - chartArea.top); + chart.outerRadius = Math.max((minSize - arcOpts.borderWidth / 2) / 2, 0); + chart.innerRadius = Math.max(opts.cutoutPercentage ? (chart.outerRadius / 100) * (opts.cutoutPercentage) : 1, 0); + chart.radiusLength = (chart.outerRadius - chart.innerRadius) / chart.getVisibleDatasetCount(); + + me.outerRadius = chart.outerRadius - (chart.radiusLength * me.index); + me.innerRadius = me.outerRadius - chart.radiusLength; + + meta.count = me.countVisibleElements(); + + helpers.each(meta.data, function(arc, index) { + me.updateElement(arc, index, reset); + }); + }, + + updateElement: function(arc, index, reset) { + var me = this; + var chart = me.chart; + var dataset = me.getDataset(); + var opts = chart.options; + var animationOpts = opts.animation; + var scale = chart.scale; + var labels = chart.data.labels; + + var circumference = me.calculateCircumference(dataset.data[index]); + var centerX = scale.xCenter; + var centerY = scale.yCenter; + + // If there is NaN data before us, we need to calculate the starting angle correctly. + // We could be way more efficient here, but its unlikely that the polar area chart will have a lot of data + var visibleCount = 0; + var meta = me.getMeta(); + for (var i = 0; i < index; ++i) { + if (!isNaN(dataset.data[i]) && !meta.data[i].hidden) { + ++visibleCount; + } + } + + // var negHalfPI = -0.5 * Math.PI; + var datasetStartAngle = opts.startAngle; + var distance = arc.hidden ? 0 : scale.getDistanceFromCenterForValue(dataset.data[index]); + var startAngle = datasetStartAngle + (circumference * visibleCount); + var endAngle = startAngle + (arc.hidden ? 0 : circumference); + + var resetRadius = animationOpts.animateScale ? 0 : scale.getDistanceFromCenterForValue(dataset.data[index]); + + helpers.extend(arc, { + // Utility + _datasetIndex: me.index, + _index: index, + _scale: scale, + + // Desired view properties + _model: { + x: centerX, + y: centerY, + innerRadius: 0, + outerRadius: reset ? resetRadius : distance, + startAngle: reset && animationOpts.animateRotate ? datasetStartAngle : startAngle, + endAngle: reset && animationOpts.animateRotate ? datasetStartAngle : endAngle, + label: helpers.valueAtIndexOrDefault(labels, index, labels[index]) + } + }); + + // Apply border and fill style + me.removeHoverStyle(arc); + + arc.pivot(); + }, + + removeHoverStyle: function(arc) { + Chart.DatasetController.prototype.removeHoverStyle.call(this, arc, this.chart.options.elements.arc); + }, + + countVisibleElements: function() { + var dataset = this.getDataset(); + var meta = this.getMeta(); + var count = 0; + + helpers.each(meta.data, function(element, index) { + if (!isNaN(dataset.data[index]) && !element.hidden) { + count++; + } + }); + + return count; + }, + + calculateCircumference: function(value) { + var count = this.getMeta().count; + if (count > 0 && !isNaN(value)) { + return (2 * Math.PI) / count; + } + return 0; + } + }); +}; + +},{"25":25,"40":40,"45":45}],20:[function(require,module,exports){ +'use strict'; + +var defaults = require(25); +var elements = require(40); +var helpers = require(45); + +defaults._set('radar', { + scale: { + type: 'radialLinear' + }, + elements: { + line: { + tension: 0 // no bezier in radar + } + } +}); + +module.exports = function(Chart) { + + Chart.controllers.radar = Chart.DatasetController.extend({ + + datasetElementType: elements.Line, + + dataElementType: elements.Point, + + linkScales: helpers.noop, + + update: function(reset) { + var me = this; + var meta = me.getMeta(); + var line = meta.dataset; + var points = meta.data; + var custom = line.custom || {}; + var dataset = me.getDataset(); + var lineElementOptions = me.chart.options.elements.line; + var scale = me.chart.scale; + + // Compatibility: If the properties are defined with only the old name, use those values + if ((dataset.tension !== undefined) && (dataset.lineTension === undefined)) { + dataset.lineTension = dataset.tension; + } + + helpers.extend(meta.dataset, { + // Utility + _datasetIndex: me.index, + _scale: scale, + // Data + _children: points, + _loop: true, + // Model + _model: { + // Appearance + tension: custom.tension ? custom.tension : helpers.valueOrDefault(dataset.lineTension, lineElementOptions.tension), + backgroundColor: custom.backgroundColor ? custom.backgroundColor : (dataset.backgroundColor || lineElementOptions.backgroundColor), + borderWidth: custom.borderWidth ? custom.borderWidth : (dataset.borderWidth || lineElementOptions.borderWidth), + borderColor: custom.borderColor ? custom.borderColor : (dataset.borderColor || lineElementOptions.borderColor), + fill: custom.fill ? custom.fill : (dataset.fill !== undefined ? dataset.fill : lineElementOptions.fill), + borderCapStyle: custom.borderCapStyle ? custom.borderCapStyle : (dataset.borderCapStyle || lineElementOptions.borderCapStyle), + borderDash: custom.borderDash ? custom.borderDash : (dataset.borderDash || lineElementOptions.borderDash), + borderDashOffset: custom.borderDashOffset ? custom.borderDashOffset : (dataset.borderDashOffset || lineElementOptions.borderDashOffset), + borderJoinStyle: custom.borderJoinStyle ? custom.borderJoinStyle : (dataset.borderJoinStyle || lineElementOptions.borderJoinStyle), + } + }); + + meta.dataset.pivot(); + + // Update Points + helpers.each(points, function(point, index) { + me.updateElement(point, index, reset); + }, me); + + // Update bezier control points + me.updateBezierControlPoints(); + }, + updateElement: function(point, index, reset) { + var me = this; + var custom = point.custom || {}; + var dataset = me.getDataset(); + var scale = me.chart.scale; + var pointElementOptions = me.chart.options.elements.point; + var pointPosition = scale.getPointPositionForValue(index, dataset.data[index]); + + // Compatibility: If the properties are defined with only the old name, use those values + if ((dataset.radius !== undefined) && (dataset.pointRadius === undefined)) { + dataset.pointRadius = dataset.radius; + } + if ((dataset.hitRadius !== undefined) && (dataset.pointHitRadius === undefined)) { + dataset.pointHitRadius = dataset.hitRadius; + } + + helpers.extend(point, { + // Utility + _datasetIndex: me.index, + _index: index, + _scale: scale, + + // Desired view properties + _model: { + x: reset ? scale.xCenter : pointPosition.x, // value not used in dataset scale, but we want a consistent API between scales + y: reset ? scale.yCenter : pointPosition.y, + + // Appearance + tension: custom.tension ? custom.tension : helpers.valueOrDefault(dataset.lineTension, me.chart.options.elements.line.tension), + radius: custom.radius ? custom.radius : helpers.valueAtIndexOrDefault(dataset.pointRadius, index, pointElementOptions.radius), + backgroundColor: custom.backgroundColor ? custom.backgroundColor : helpers.valueAtIndexOrDefault(dataset.pointBackgroundColor, index, pointElementOptions.backgroundColor), + borderColor: custom.borderColor ? custom.borderColor : helpers.valueAtIndexOrDefault(dataset.pointBorderColor, index, pointElementOptions.borderColor), + borderWidth: custom.borderWidth ? custom.borderWidth : helpers.valueAtIndexOrDefault(dataset.pointBorderWidth, index, pointElementOptions.borderWidth), + pointStyle: custom.pointStyle ? custom.pointStyle : helpers.valueAtIndexOrDefault(dataset.pointStyle, index, pointElementOptions.pointStyle), + + // Tooltip + hitRadius: custom.hitRadius ? custom.hitRadius : helpers.valueAtIndexOrDefault(dataset.pointHitRadius, index, pointElementOptions.hitRadius) + } + }); + + point._model.skip = custom.skip ? custom.skip : (isNaN(point._model.x) || isNaN(point._model.y)); + }, + updateBezierControlPoints: function() { + var chartArea = this.chart.chartArea; + var meta = this.getMeta(); + + helpers.each(meta.data, function(point, index) { + var model = point._model; + var controlPoints = helpers.splineCurve( + helpers.previousItem(meta.data, index, true)._model, + model, + helpers.nextItem(meta.data, index, true)._model, + model.tension + ); + + // Prevent the bezier going outside of the bounds of the graph + model.controlPointPreviousX = Math.max(Math.min(controlPoints.previous.x, chartArea.right), chartArea.left); + model.controlPointPreviousY = Math.max(Math.min(controlPoints.previous.y, chartArea.bottom), chartArea.top); + + model.controlPointNextX = Math.max(Math.min(controlPoints.next.x, chartArea.right), chartArea.left); + model.controlPointNextY = Math.max(Math.min(controlPoints.next.y, chartArea.bottom), chartArea.top); + + // Now pivot the point for animation + point.pivot(); + }); + }, + + setHoverStyle: function(point) { + // Point + var dataset = this.chart.data.datasets[point._datasetIndex]; + var custom = point.custom || {}; + var index = point._index; + var model = point._model; + + model.radius = custom.hoverRadius ? custom.hoverRadius : helpers.valueAtIndexOrDefault(dataset.pointHoverRadius, index, this.chart.options.elements.point.hoverRadius); + model.backgroundColor = custom.hoverBackgroundColor ? custom.hoverBackgroundColor : helpers.valueAtIndexOrDefault(dataset.pointHoverBackgroundColor, index, helpers.getHoverColor(model.backgroundColor)); + model.borderColor = custom.hoverBorderColor ? custom.hoverBorderColor : helpers.valueAtIndexOrDefault(dataset.pointHoverBorderColor, index, helpers.getHoverColor(model.borderColor)); + model.borderWidth = custom.hoverBorderWidth ? custom.hoverBorderWidth : helpers.valueAtIndexOrDefault(dataset.pointHoverBorderWidth, index, model.borderWidth); + }, + + removeHoverStyle: function(point) { + var dataset = this.chart.data.datasets[point._datasetIndex]; + var custom = point.custom || {}; + var index = point._index; + var model = point._model; + var pointElementOptions = this.chart.options.elements.point; + + model.radius = custom.radius ? custom.radius : helpers.valueAtIndexOrDefault(dataset.pointRadius, index, pointElementOptions.radius); + model.backgroundColor = custom.backgroundColor ? custom.backgroundColor : helpers.valueAtIndexOrDefault(dataset.pointBackgroundColor, index, pointElementOptions.backgroundColor); + model.borderColor = custom.borderColor ? custom.borderColor : helpers.valueAtIndexOrDefault(dataset.pointBorderColor, index, pointElementOptions.borderColor); + model.borderWidth = custom.borderWidth ? custom.borderWidth : helpers.valueAtIndexOrDefault(dataset.pointBorderWidth, index, pointElementOptions.borderWidth); + } + }); +}; + +},{"25":25,"40":40,"45":45}],21:[function(require,module,exports){ +'use strict'; + +var defaults = require(25); + +defaults._set('scatter', { + hover: { + mode: 'single' + }, + + scales: { + xAxes: [{ + id: 'x-axis-1', // need an ID so datasets can reference the scale + type: 'linear', // scatter should not use a category axis + position: 'bottom' + }], + yAxes: [{ + id: 'y-axis-1', + type: 'linear', + position: 'left' + }] + }, + + showLines: false, + + tooltips: { + callbacks: { + title: function() { + return ''; // doesn't make sense for scatter since data are formatted as a point + }, + label: function(item) { + return '(' + item.xLabel + ', ' + item.yLabel + ')'; + } + } + } +}); + +module.exports = function(Chart) { + + // Scatter charts use line controllers + Chart.controllers.scatter = Chart.controllers.line; + +}; + +},{"25":25}],22:[function(require,module,exports){ +/* global window: false */ +'use strict'; + +var defaults = require(25); +var Element = require(26); +var helpers = require(45); + +defaults._set('global', { + animation: { + duration: 1000, + easing: 'easeOutQuart', + onProgress: helpers.noop, + onComplete: helpers.noop + } +}); + +module.exports = function(Chart) { + + Chart.Animation = Element.extend({ + chart: null, // the animation associated chart instance + currentStep: 0, // the current animation step + numSteps: 60, // default number of steps + easing: '', // the easing to use for this animation + render: null, // render function used by the animation service + + onAnimationProgress: null, // user specified callback to fire on each step of the animation + onAnimationComplete: null, // user specified callback to fire when the animation finishes + }); + + Chart.animationService = { + frameDuration: 17, + animations: [], + dropFrames: 0, + request: null, + + /** + * @param {Chart} chart - The chart to animate. + * @param {Chart.Animation} animation - The animation that we will animate. + * @param {Number} duration - The animation duration in ms. + * @param {Boolean} lazy - if true, the chart is not marked as animating to enable more responsive interactions + */ + addAnimation: function(chart, animation, duration, lazy) { + var animations = this.animations; + var i, ilen; + + animation.chart = chart; + + if (!lazy) { + chart.animating = true; + } + + for (i = 0, ilen = animations.length; i < ilen; ++i) { + if (animations[i].chart === chart) { + animations[i] = animation; + return; + } + } + + animations.push(animation); + + // If there are no animations queued, manually kickstart a digest, for lack of a better word + if (animations.length === 1) { + this.requestAnimationFrame(); + } + }, + + cancelAnimation: function(chart) { + var index = helpers.findIndex(this.animations, function(animation) { + return animation.chart === chart; + }); + + if (index !== -1) { + this.animations.splice(index, 1); + chart.animating = false; + } + }, + + requestAnimationFrame: function() { + var me = this; + if (me.request === null) { + // Skip animation frame requests until the active one is executed. + // This can happen when processing mouse events, e.g. 'mousemove' + // and 'mouseout' events will trigger multiple renders. + me.request = helpers.requestAnimFrame.call(window, function() { + me.request = null; + me.startDigest(); + }); + } + }, + + /** + * @private + */ + startDigest: function() { + var me = this; + var startTime = Date.now(); + var framesToDrop = 0; + + if (me.dropFrames > 1) { + framesToDrop = Math.floor(me.dropFrames); + me.dropFrames = me.dropFrames % 1; + } + + me.advance(1 + framesToDrop); + + var endTime = Date.now(); + + me.dropFrames += (endTime - startTime) / me.frameDuration; + + // Do we have more stuff to animate? + if (me.animations.length > 0) { + me.requestAnimationFrame(); + } + }, + + /** + * @private + */ + advance: function(count) { + var animations = this.animations; + var animation, chart; + var i = 0; + + while (i < animations.length) { + animation = animations[i]; + chart = animation.chart; + + animation.currentStep = (animation.currentStep || 0) + count; + animation.currentStep = Math.min(animation.currentStep, animation.numSteps); + + helpers.callback(animation.render, [chart, animation], chart); + helpers.callback(animation.onAnimationProgress, [animation], chart); + + if (animation.currentStep >= animation.numSteps) { + helpers.callback(animation.onAnimationComplete, [animation], chart); + chart.animating = false; + animations.splice(i, 1); + } else { + ++i; + } + } + } + }; + + /** + * Provided for backward compatibility, use Chart.Animation instead + * @prop Chart.Animation#animationObject + * @deprecated since version 2.6.0 + * @todo remove at version 3 + */ + Object.defineProperty(Chart.Animation.prototype, 'animationObject', { + get: function() { + return this; + } + }); + + /** + * Provided for backward compatibility, use Chart.Animation#chart instead + * @prop Chart.Animation#chartInstance + * @deprecated since version 2.6.0 + * @todo remove at version 3 + */ + Object.defineProperty(Chart.Animation.prototype, 'chartInstance', { + get: function() { + return this.chart; + }, + set: function(value) { + this.chart = value; + } + }); + +}; + +},{"25":25,"26":26,"45":45}],23:[function(require,module,exports){ +'use strict'; + +var defaults = require(25); +var helpers = require(45); +var Interaction = require(28); +var platform = require(48); + +module.exports = function(Chart) { + var plugins = Chart.plugins; + + // Create a dictionary of chart types, to allow for extension of existing types + Chart.types = {}; + + // Store a reference to each instance - allowing us to globally resize chart instances on window resize. + // Destroy method on the chart will remove the instance of the chart from this reference. + Chart.instances = {}; + + // Controllers available for dataset visualization eg. bar, line, slice, etc. + Chart.controllers = {}; + + /** + * Initializes the given config with global and chart default values. + */ + function initConfig(config) { + config = config || {}; + + // Do NOT use configMerge() for the data object because this method merges arrays + // and so would change references to labels and datasets, preventing data updates. + var data = config.data = config.data || {}; + data.datasets = data.datasets || []; + data.labels = data.labels || []; + + config.options = helpers.configMerge( + defaults.global, + defaults[config.type], + config.options || {}); + + return config; + } + + /** + * Updates the config of the chart + * @param chart {Chart} chart to update the options for + */ + function updateConfig(chart) { + var newOptions = chart.options; + + // Update Scale(s) with options + if (newOptions.scale) { + chart.scale.options = newOptions.scale; + } else if (newOptions.scales) { + newOptions.scales.xAxes.concat(newOptions.scales.yAxes).forEach(function(scaleOptions) { + chart.scales[scaleOptions.id].options = scaleOptions; + }); + } + + // Tooltip + chart.tooltip._options = newOptions.tooltips; + } + + function positionIsHorizontal(position) { + return position === 'top' || position === 'bottom'; + } + + helpers.extend(Chart.prototype, /** @lends Chart */ { + /** + * @private + */ + construct: function(item, config) { + var me = this; + + config = initConfig(config); + + var context = platform.acquireContext(item, config); + var canvas = context && context.canvas; + var height = canvas && canvas.height; + var width = canvas && canvas.width; + + me.id = helpers.uid(); + me.ctx = context; + me.canvas = canvas; + me.config = config; + me.width = width; + me.height = height; + me.aspectRatio = height ? width / height : null; + me.options = config.options; + me._bufferedRender = false; + + /** + * Provided for backward compatibility, Chart and Chart.Controller have been merged, + * the "instance" still need to be defined since it might be called from plugins. + * @prop Chart#chart + * @deprecated since version 2.6.0 + * @todo remove at version 3 + * @private + */ + me.chart = me; + me.controller = me; // chart.chart.controller #inception + + // Add the chart instance to the global namespace + Chart.instances[me.id] = me; + + // Define alias to the config data: `chart.data === chart.config.data` + Object.defineProperty(me, 'data', { + get: function() { + return me.config.data; + }, + set: function(value) { + me.config.data = value; + } + }); + + if (!context || !canvas) { + // The given item is not a compatible context2d element, let's return before finalizing + // the chart initialization but after setting basic chart / controller properties that + // can help to figure out that the chart is not valid (e.g chart.canvas !== null); + // https://github.com/chartjs/Chart.js/issues/2807 + console.error("Failed to create chart: can't acquire context from the given item"); + return; + } + + me.initialize(); + me.update(); + }, + + /** + * @private + */ + initialize: function() { + var me = this; + + // Before init plugin notification + plugins.notify(me, 'beforeInit'); + + helpers.retinaScale(me, me.options.devicePixelRatio); + + me.bindEvents(); + + if (me.options.responsive) { + // Initial resize before chart draws (must be silent to preserve initial animations). + me.resize(true); + } + + // Make sure scales have IDs and are built before we build any controllers. + me.ensureScalesHaveIDs(); + me.buildScales(); + me.initToolTip(); + + // After init plugin notification + plugins.notify(me, 'afterInit'); + + return me; + }, + + clear: function() { + helpers.canvas.clear(this); + return this; + }, + + stop: function() { + // Stops any current animation loop occurring + Chart.animationService.cancelAnimation(this); + return this; + }, + + resize: function(silent) { + var me = this; + var options = me.options; + var canvas = me.canvas; + var aspectRatio = (options.maintainAspectRatio && me.aspectRatio) || null; + + // the canvas render width and height will be casted to integers so make sure that + // the canvas display style uses the same integer values to avoid blurring effect. + + // Set to 0 instead of canvas.size because the size defaults to 300x150 if the element is collased + var newWidth = Math.max(0, Math.floor(helpers.getMaximumWidth(canvas))); + var newHeight = Math.max(0, Math.floor(aspectRatio ? newWidth / aspectRatio : helpers.getMaximumHeight(canvas))); + + if (me.width === newWidth && me.height === newHeight) { + return; + } + + canvas.width = me.width = newWidth; + canvas.height = me.height = newHeight; + canvas.style.width = newWidth + 'px'; + canvas.style.height = newHeight + 'px'; + + helpers.retinaScale(me, options.devicePixelRatio); + + if (!silent) { + // Notify any plugins about the resize + var newSize = {width: newWidth, height: newHeight}; + plugins.notify(me, 'resize', [newSize]); + + // Notify of resize + if (me.options.onResize) { + me.options.onResize(me, newSize); + } + + me.stop(); + me.update(me.options.responsiveAnimationDuration); + } + }, + + ensureScalesHaveIDs: function() { + var options = this.options; + var scalesOptions = options.scales || {}; + var scaleOptions = options.scale; + + helpers.each(scalesOptions.xAxes, function(xAxisOptions, index) { + xAxisOptions.id = xAxisOptions.id || ('x-axis-' + index); + }); + + helpers.each(scalesOptions.yAxes, function(yAxisOptions, index) { + yAxisOptions.id = yAxisOptions.id || ('y-axis-' + index); + }); + + if (scaleOptions) { + scaleOptions.id = scaleOptions.id || 'scale'; + } + }, + + /** + * Builds a map of scale ID to scale object for future lookup. + */ + buildScales: function() { + var me = this; + var options = me.options; + var scales = me.scales = {}; + var items = []; + + if (options.scales) { + items = items.concat( + (options.scales.xAxes || []).map(function(xAxisOptions) { + return {options: xAxisOptions, dtype: 'category', dposition: 'bottom'}; + }), + (options.scales.yAxes || []).map(function(yAxisOptions) { + return {options: yAxisOptions, dtype: 'linear', dposition: 'left'}; + }) + ); + } + + if (options.scale) { + items.push({ + options: options.scale, + dtype: 'radialLinear', + isDefault: true, + dposition: 'chartArea' + }); + } + + helpers.each(items, function(item) { + var scaleOptions = item.options; + var scaleType = helpers.valueOrDefault(scaleOptions.type, item.dtype); + var scaleClass = Chart.scaleService.getScaleConstructor(scaleType); + if (!scaleClass) { + return; + } + + if (positionIsHorizontal(scaleOptions.position) !== positionIsHorizontal(item.dposition)) { + scaleOptions.position = item.dposition; + } + + var scale = new scaleClass({ + id: scaleOptions.id, + options: scaleOptions, + ctx: me.ctx, + chart: me + }); + + scales[scale.id] = scale; + scale.mergeTicksOptions(); + + // TODO(SB): I think we should be able to remove this custom case (options.scale) + // and consider it as a regular scale part of the "scales"" map only! This would + // make the logic easier and remove some useless? custom code. + if (item.isDefault) { + me.scale = scale; + } + }); + + Chart.scaleService.addScalesToLayout(this); + }, + + buildOrUpdateControllers: function() { + var me = this; + var types = []; + var newControllers = []; + + helpers.each(me.data.datasets, function(dataset, datasetIndex) { + var meta = me.getDatasetMeta(datasetIndex); + var type = dataset.type || me.config.type; + + if (meta.type && meta.type !== type) { + me.destroyDatasetMeta(datasetIndex); + meta = me.getDatasetMeta(datasetIndex); + } + meta.type = type; + + types.push(meta.type); + + if (meta.controller) { + meta.controller.updateIndex(datasetIndex); + } else { + var ControllerClass = Chart.controllers[meta.type]; + if (ControllerClass === undefined) { + throw new Error('"' + meta.type + '" is not a chart type.'); + } + + meta.controller = new ControllerClass(me, datasetIndex); + newControllers.push(meta.controller); + } + }, me); + + return newControllers; + }, + + /** + * Reset the elements of all datasets + * @private + */ + resetElements: function() { + var me = this; + helpers.each(me.data.datasets, function(dataset, datasetIndex) { + me.getDatasetMeta(datasetIndex).controller.reset(); + }, me); + }, + + /** + * Resets the chart back to it's state before the initial animation + */ + reset: function() { + this.resetElements(); + this.tooltip.initialize(); + }, + + update: function(config) { + var me = this; + + if (!config || typeof config !== 'object') { + // backwards compatibility + config = { + duration: config, + lazy: arguments[1] + }; + } + + updateConfig(me); + + if (plugins.notify(me, 'beforeUpdate') === false) { + return; + } + + // In case the entire data object changed + me.tooltip._data = me.data; + + // Make sure dataset controllers are updated and new controllers are reset + var newControllers = me.buildOrUpdateControllers(); + + // Make sure all dataset controllers have correct meta data counts + helpers.each(me.data.datasets, function(dataset, datasetIndex) { + me.getDatasetMeta(datasetIndex).controller.buildOrUpdateElements(); + }, me); + + me.updateLayout(); + + // Can only reset the new controllers after the scales have been updated + helpers.each(newControllers, function(controller) { + controller.reset(); + }); + + me.updateDatasets(); + + // Need to reset tooltip in case it is displayed with elements that are removed + // after update. + me.tooltip.initialize(); + + // Last active contains items that were previously in the tooltip. + // When we reset the tooltip, we need to clear it + me.lastActive = []; + + // Do this before render so that any plugins that need final scale updates can use it + plugins.notify(me, 'afterUpdate'); + + if (me._bufferedRender) { + me._bufferedRequest = { + duration: config.duration, + easing: config.easing, + lazy: config.lazy + }; + } else { + me.render(config); + } + }, + + /** + * Updates the chart layout unless a plugin returns `false` to the `beforeLayout` + * hook, in which case, plugins will not be called on `afterLayout`. + * @private + */ + updateLayout: function() { + var me = this; + + if (plugins.notify(me, 'beforeLayout') === false) { + return; + } + + Chart.layoutService.update(this, this.width, this.height); + + /** + * Provided for backward compatibility, use `afterLayout` instead. + * @method IPlugin#afterScaleUpdate + * @deprecated since version 2.5.0 + * @todo remove at version 3 + * @private + */ + plugins.notify(me, 'afterScaleUpdate'); + plugins.notify(me, 'afterLayout'); + }, + + /** + * Updates all datasets unless a plugin returns `false` to the `beforeDatasetsUpdate` + * hook, in which case, plugins will not be called on `afterDatasetsUpdate`. + * @private + */ + updateDatasets: function() { + var me = this; + + if (plugins.notify(me, 'beforeDatasetsUpdate') === false) { + return; + } + + for (var i = 0, ilen = me.data.datasets.length; i < ilen; ++i) { + me.updateDataset(i); + } + + plugins.notify(me, 'afterDatasetsUpdate'); + }, + + /** + * Updates dataset at index unless a plugin returns `false` to the `beforeDatasetUpdate` + * hook, in which case, plugins will not be called on `afterDatasetUpdate`. + * @private + */ + updateDataset: function(index) { + var me = this; + var meta = me.getDatasetMeta(index); + var args = { + meta: meta, + index: index + }; + + if (plugins.notify(me, 'beforeDatasetUpdate', [args]) === false) { + return; + } + + meta.controller.update(); + + plugins.notify(me, 'afterDatasetUpdate', [args]); + }, + + render: function(config) { + var me = this; + + if (!config || typeof config !== 'object') { + // backwards compatibility + config = { + duration: config, + lazy: arguments[1] + }; + } + + var duration = config.duration; + var lazy = config.lazy; + + if (plugins.notify(me, 'beforeRender') === false) { + return; + } + + var animationOptions = me.options.animation; + var onComplete = function(animation) { + plugins.notify(me, 'afterRender'); + helpers.callback(animationOptions && animationOptions.onComplete, [animation], me); + }; + + if (animationOptions && ((typeof duration !== 'undefined' && duration !== 0) || (typeof duration === 'undefined' && animationOptions.duration !== 0))) { + var animation = new Chart.Animation({ + numSteps: (duration || animationOptions.duration) / 16.66, // 60 fps + easing: config.easing || animationOptions.easing, + + render: function(chart, animationObject) { + var easingFunction = helpers.easing.effects[animationObject.easing]; + var currentStep = animationObject.currentStep; + var stepDecimal = currentStep / animationObject.numSteps; + + chart.draw(easingFunction(stepDecimal), stepDecimal, currentStep); + }, + + onAnimationProgress: animationOptions.onProgress, + onAnimationComplete: onComplete + }); + + Chart.animationService.addAnimation(me, animation, duration, lazy); + } else { + me.draw(); + + // See https://github.com/chartjs/Chart.js/issues/3781 + onComplete(new Chart.Animation({numSteps: 0, chart: me})); + } + + return me; + }, + + draw: function(easingValue) { + var me = this; + + me.clear(); + + if (helpers.isNullOrUndef(easingValue)) { + easingValue = 1; + } + + me.transition(easingValue); + + if (plugins.notify(me, 'beforeDraw', [easingValue]) === false) { + return; + } + + // Draw all the scales + helpers.each(me.boxes, function(box) { + box.draw(me.chartArea); + }, me); + + if (me.scale) { + me.scale.draw(); + } + + me.drawDatasets(easingValue); + me._drawTooltip(easingValue); + + plugins.notify(me, 'afterDraw', [easingValue]); + }, + + /** + * @private + */ + transition: function(easingValue) { + var me = this; + + for (var i = 0, ilen = (me.data.datasets || []).length; i < ilen; ++i) { + if (me.isDatasetVisible(i)) { + me.getDatasetMeta(i).controller.transition(easingValue); + } + } + + me.tooltip.transition(easingValue); + }, + + /** + * Draws all datasets unless a plugin returns `false` to the `beforeDatasetsDraw` + * hook, in which case, plugins will not be called on `afterDatasetsDraw`. + * @private + */ + drawDatasets: function(easingValue) { + var me = this; + + if (plugins.notify(me, 'beforeDatasetsDraw', [easingValue]) === false) { + return; + } + + // Draw datasets reversed to support proper line stacking + for (var i = (me.data.datasets || []).length - 1; i >= 0; --i) { + if (me.isDatasetVisible(i)) { + me.drawDataset(i, easingValue); + } + } + + plugins.notify(me, 'afterDatasetsDraw', [easingValue]); + }, + + /** + * Draws dataset at index unless a plugin returns `false` to the `beforeDatasetDraw` + * hook, in which case, plugins will not be called on `afterDatasetDraw`. + * @private + */ + drawDataset: function(index, easingValue) { + var me = this; + var meta = me.getDatasetMeta(index); + var args = { + meta: meta, + index: index, + easingValue: easingValue + }; + + if (plugins.notify(me, 'beforeDatasetDraw', [args]) === false) { + return; + } + + meta.controller.draw(easingValue); + + plugins.notify(me, 'afterDatasetDraw', [args]); + }, + + /** + * Draws tooltip unless a plugin returns `false` to the `beforeTooltipDraw` + * hook, in which case, plugins will not be called on `afterTooltipDraw`. + * @private + */ + _drawTooltip: function(easingValue) { + var me = this; + var tooltip = me.tooltip; + var args = { + tooltip: tooltip, + easingValue: easingValue + }; + + if (plugins.notify(me, 'beforeTooltipDraw', [args]) === false) { + return; + } + + tooltip.draw(); + + plugins.notify(me, 'afterTooltipDraw', [args]); + }, + + // Get the single element that was clicked on + // @return : An object containing the dataset index and element index of the matching element. Also contains the rectangle that was draw + getElementAtEvent: function(e) { + return Interaction.modes.single(this, e); + }, + + getElementsAtEvent: function(e) { + return Interaction.modes.label(this, e, {intersect: true}); + }, + + getElementsAtXAxis: function(e) { + return Interaction.modes['x-axis'](this, e, {intersect: true}); + }, + + getElementsAtEventForMode: function(e, mode, options) { + var method = Interaction.modes[mode]; + if (typeof method === 'function') { + return method(this, e, options); + } + + return []; + }, + + getDatasetAtEvent: function(e) { + return Interaction.modes.dataset(this, e, {intersect: true}); + }, + + getDatasetMeta: function(datasetIndex) { + var me = this; + var dataset = me.data.datasets[datasetIndex]; + if (!dataset._meta) { + dataset._meta = {}; + } + + var meta = dataset._meta[me.id]; + if (!meta) { + meta = dataset._meta[me.id] = { + type: null, + data: [], + dataset: null, + controller: null, + hidden: null, // See isDatasetVisible() comment + xAxisID: null, + yAxisID: null + }; + } + + return meta; + }, + + getVisibleDatasetCount: function() { + var count = 0; + for (var i = 0, ilen = this.data.datasets.length; i < ilen; ++i) { + if (this.isDatasetVisible(i)) { + count++; + } + } + return count; + }, + + isDatasetVisible: function(datasetIndex) { + var meta = this.getDatasetMeta(datasetIndex); + + // meta.hidden is a per chart dataset hidden flag override with 3 states: if true or false, + // the dataset.hidden value is ignored, else if null, the dataset hidden state is returned. + return typeof meta.hidden === 'boolean' ? !meta.hidden : !this.data.datasets[datasetIndex].hidden; + }, + + generateLegend: function() { + return this.options.legendCallback(this); + }, + + /** + * @private + */ + destroyDatasetMeta: function(datasetIndex) { + var id = this.id; + var dataset = this.data.datasets[datasetIndex]; + var meta = dataset._meta && dataset._meta[id]; + + if (meta) { + meta.controller.destroy(); + delete dataset._meta[id]; + } + }, + + destroy: function() { + var me = this; + var canvas = me.canvas; + var i, ilen; + + me.stop(); + + // dataset controllers need to cleanup associated data + for (i = 0, ilen = me.data.datasets.length; i < ilen; ++i) { + me.destroyDatasetMeta(i); + } + + if (canvas) { + me.unbindEvents(); + helpers.canvas.clear(me); + platform.releaseContext(me.ctx); + me.canvas = null; + me.ctx = null; + } + + plugins.notify(me, 'destroy'); + + delete Chart.instances[me.id]; + }, + + toBase64Image: function() { + return this.canvas.toDataURL.apply(this.canvas, arguments); + }, + + initToolTip: function() { + var me = this; + me.tooltip = new Chart.Tooltip({ + _chart: me, + _chartInstance: me, // deprecated, backward compatibility + _data: me.data, + _options: me.options.tooltips + }, me); + }, + + /** + * @private + */ + bindEvents: function() { + var me = this; + var listeners = me._listeners = {}; + var listener = function() { + me.eventHandler.apply(me, arguments); + }; + + helpers.each(me.options.events, function(type) { + platform.addEventListener(me, type, listener); + listeners[type] = listener; + }); + + // Elements used to detect size change should not be injected for non responsive charts. + // See https://github.com/chartjs/Chart.js/issues/2210 + if (me.options.responsive) { + listener = function() { + me.resize(); + }; + + platform.addEventListener(me, 'resize', listener); + listeners.resize = listener; + } + }, + + /** + * @private + */ + unbindEvents: function() { + var me = this; + var listeners = me._listeners; + if (!listeners) { + return; + } + + delete me._listeners; + helpers.each(listeners, function(listener, type) { + platform.removeEventListener(me, type, listener); + }); + }, + + updateHoverStyle: function(elements, mode, enabled) { + var method = enabled ? 'setHoverStyle' : 'removeHoverStyle'; + var element, i, ilen; + + for (i = 0, ilen = elements.length; i < ilen; ++i) { + element = elements[i]; + if (element) { + this.getDatasetMeta(element._datasetIndex).controller[method](element); + } + } + }, + + /** + * @private + */ + eventHandler: function(e) { + var me = this; + var tooltip = me.tooltip; + + if (plugins.notify(me, 'beforeEvent', [e]) === false) { + return; + } + + // Buffer any update calls so that renders do not occur + me._bufferedRender = true; + me._bufferedRequest = null; + + var changed = me.handleEvent(e); + changed |= tooltip && tooltip.handleEvent(e); + + plugins.notify(me, 'afterEvent', [e]); + + var bufferedRequest = me._bufferedRequest; + if (bufferedRequest) { + // If we have an update that was triggered, we need to do a normal render + me.render(bufferedRequest); + } else if (changed && !me.animating) { + // If entering, leaving, or changing elements, animate the change via pivot + me.stop(); + + // We only need to render at this point. Updating will cause scales to be + // recomputed generating flicker & using more memory than necessary. + me.render(me.options.hover.animationDuration, true); + } + + me._bufferedRender = false; + me._bufferedRequest = null; + + return me; + }, + + /** + * Handle an event + * @private + * @param {IEvent} event the event to handle + * @return {Boolean} true if the chart needs to re-render + */ + handleEvent: function(e) { + var me = this; + var options = me.options || {}; + var hoverOptions = options.hover; + var changed = false; + + me.lastActive = me.lastActive || []; + + // Find Active Elements for hover and tooltips + if (e.type === 'mouseout') { + me.active = []; + } else { + me.active = me.getElementsAtEventForMode(e, hoverOptions.mode, hoverOptions); + } + + // Invoke onHover hook + // Need to call with native event here to not break backwards compatibility + helpers.callback(options.onHover || options.hover.onHover, [e.native, me.active], me); + + if (e.type === 'mouseup' || e.type === 'click') { + if (options.onClick) { + // Use e.native here for backwards compatibility + options.onClick.call(me, e.native, me.active); + } + } + + // Remove styling for last active (even if it may still be active) + if (me.lastActive.length) { + me.updateHoverStyle(me.lastActive, hoverOptions.mode, false); + } + + // Built in hover styling + if (me.active.length && hoverOptions.mode) { + me.updateHoverStyle(me.active, hoverOptions.mode, true); + } + + changed = !helpers.arrayEquals(me.active, me.lastActive); + + // Remember Last Actives + me.lastActive = me.active; + + return changed; + } + }); + + /** + * Provided for backward compatibility, use Chart instead. + * @class Chart.Controller + * @deprecated since version 2.6.0 + * @todo remove at version 3 + * @private + */ + Chart.Controller = Chart; +}; + +},{"25":25,"28":28,"45":45,"48":48}],24:[function(require,module,exports){ +'use strict'; + +var helpers = require(45); + +module.exports = function(Chart) { + + var arrayEvents = ['push', 'pop', 'shift', 'splice', 'unshift']; + + /** + * Hooks the array methods that add or remove values ('push', pop', 'shift', 'splice', + * 'unshift') and notify the listener AFTER the array has been altered. Listeners are + * called on the 'onData*' callbacks (e.g. onDataPush, etc.) with same arguments. + */ + function listenArrayEvents(array, listener) { + if (array._chartjs) { + array._chartjs.listeners.push(listener); + return; + } + + Object.defineProperty(array, '_chartjs', { + configurable: true, + enumerable: false, + value: { + listeners: [listener] + } + }); + + arrayEvents.forEach(function(key) { + var method = 'onData' + key.charAt(0).toUpperCase() + key.slice(1); + var base = array[key]; + + Object.defineProperty(array, key, { + configurable: true, + enumerable: false, + value: function() { + var args = Array.prototype.slice.call(arguments); + var res = base.apply(this, args); + + helpers.each(array._chartjs.listeners, function(object) { + if (typeof object[method] === 'function') { + object[method].apply(object, args); + } + }); + + return res; + } + }); + }); + } + + /** + * Removes the given array event listener and cleanup extra attached properties (such as + * the _chartjs stub and overridden methods) if array doesn't have any more listeners. + */ + function unlistenArrayEvents(array, listener) { + var stub = array._chartjs; + if (!stub) { + return; + } + + var listeners = stub.listeners; + var index = listeners.indexOf(listener); + if (index !== -1) { + listeners.splice(index, 1); + } + + if (listeners.length > 0) { + return; + } + + arrayEvents.forEach(function(key) { + delete array[key]; + }); + + delete array._chartjs; + } + + // Base class for all dataset controllers (line, bar, etc) + Chart.DatasetController = function(chart, datasetIndex) { + this.initialize(chart, datasetIndex); + }; + + helpers.extend(Chart.DatasetController.prototype, { + + /** + * Element type used to generate a meta dataset (e.g. Chart.element.Line). + * @type {Chart.core.element} + */ + datasetElementType: null, + + /** + * Element type used to generate a meta data (e.g. Chart.element.Point). + * @type {Chart.core.element} + */ + dataElementType: null, + + initialize: function(chart, datasetIndex) { + var me = this; + me.chart = chart; + me.index = datasetIndex; + me.linkScales(); + me.addElements(); + }, + + updateIndex: function(datasetIndex) { + this.index = datasetIndex; + }, + + linkScales: function() { + var me = this; + var meta = me.getMeta(); + var dataset = me.getDataset(); + + if (meta.xAxisID === null) { + meta.xAxisID = dataset.xAxisID || me.chart.options.scales.xAxes[0].id; + } + if (meta.yAxisID === null) { + meta.yAxisID = dataset.yAxisID || me.chart.options.scales.yAxes[0].id; + } + }, + + getDataset: function() { + return this.chart.data.datasets[this.index]; + }, + + getMeta: function() { + return this.chart.getDatasetMeta(this.index); + }, + + getScaleForId: function(scaleID) { + return this.chart.scales[scaleID]; + }, + + reset: function() { + this.update(true); + }, + + /** + * @private + */ + destroy: function() { + if (this._data) { + unlistenArrayEvents(this._data, this); + } + }, + + createMetaDataset: function() { + var me = this; + var type = me.datasetElementType; + return type && new type({ + _chart: me.chart, + _datasetIndex: me.index + }); + }, + + createMetaData: function(index) { + var me = this; + var type = me.dataElementType; + return type && new type({ + _chart: me.chart, + _datasetIndex: me.index, + _index: index + }); + }, + + addElements: function() { + var me = this; + var meta = me.getMeta(); + var data = me.getDataset().data || []; + var metaData = meta.data; + var i, ilen; + + for (i = 0, ilen = data.length; i < ilen; ++i) { + metaData[i] = metaData[i] || me.createMetaData(i); + } + + meta.dataset = meta.dataset || me.createMetaDataset(); + }, + + addElementAndReset: function(index) { + var element = this.createMetaData(index); + this.getMeta().data.splice(index, 0, element); + this.updateElement(element, index, true); + }, + + buildOrUpdateElements: function() { + var me = this; + var dataset = me.getDataset(); + var data = dataset.data || (dataset.data = []); + + // In order to correctly handle data addition/deletion animation (an thus simulate + // real-time charts), we need to monitor these data modifications and synchronize + // the internal meta data accordingly. + if (me._data !== data) { + if (me._data) { + // This case happens when the user replaced the data array instance. + unlistenArrayEvents(me._data, me); + } + + listenArrayEvents(data, me); + me._data = data; + } + + // Re-sync meta data in case the user replaced the data array or if we missed + // any updates and so make sure that we handle number of datapoints changing. + me.resyncElements(); + }, + + update: helpers.noop, + + transition: function(easingValue) { + var meta = this.getMeta(); + var elements = meta.data || []; + var ilen = elements.length; + var i = 0; + + for (; i < ilen; ++i) { + elements[i].transition(easingValue); + } + + if (meta.dataset) { + meta.dataset.transition(easingValue); + } + }, + + draw: function() { + var meta = this.getMeta(); + var elements = meta.data || []; + var ilen = elements.length; + var i = 0; + + if (meta.dataset) { + meta.dataset.draw(); + } + + for (; i < ilen; ++i) { + elements[i].draw(); + } + }, + + removeHoverStyle: function(element, elementOpts) { + var dataset = this.chart.data.datasets[element._datasetIndex]; + var index = element._index; + var custom = element.custom || {}; + var valueOrDefault = helpers.valueAtIndexOrDefault; + var model = element._model; + + model.backgroundColor = custom.backgroundColor ? custom.backgroundColor : valueOrDefault(dataset.backgroundColor, index, elementOpts.backgroundColor); + model.borderColor = custom.borderColor ? custom.borderColor : valueOrDefault(dataset.borderColor, index, elementOpts.borderColor); + model.borderWidth = custom.borderWidth ? custom.borderWidth : valueOrDefault(dataset.borderWidth, index, elementOpts.borderWidth); + }, + + setHoverStyle: function(element) { + var dataset = this.chart.data.datasets[element._datasetIndex]; + var index = element._index; + var custom = element.custom || {}; + var valueOrDefault = helpers.valueAtIndexOrDefault; + var getHoverColor = helpers.getHoverColor; + var model = element._model; + + model.backgroundColor = custom.hoverBackgroundColor ? custom.hoverBackgroundColor : valueOrDefault(dataset.hoverBackgroundColor, index, getHoverColor(model.backgroundColor)); + model.borderColor = custom.hoverBorderColor ? custom.hoverBorderColor : valueOrDefault(dataset.hoverBorderColor, index, getHoverColor(model.borderColor)); + model.borderWidth = custom.hoverBorderWidth ? custom.hoverBorderWidth : valueOrDefault(dataset.hoverBorderWidth, index, model.borderWidth); + }, + + /** + * @private + */ + resyncElements: function() { + var me = this; + var meta = me.getMeta(); + var data = me.getDataset().data; + var numMeta = meta.data.length; + var numData = data.length; + + if (numData < numMeta) { + meta.data.splice(numData, numMeta - numData); + } else if (numData > numMeta) { + me.insertElements(numMeta, numData - numMeta); + } + }, + + /** + * @private + */ + insertElements: function(start, count) { + for (var i = 0; i < count; ++i) { + this.addElementAndReset(start + i); + } + }, + + /** + * @private + */ + onDataPush: function() { + this.insertElements(this.getDataset().data.length - 1, arguments.length); + }, + + /** + * @private + */ + onDataPop: function() { + this.getMeta().data.pop(); + }, + + /** + * @private + */ + onDataShift: function() { + this.getMeta().data.shift(); + }, + + /** + * @private + */ + onDataSplice: function(start, count) { + this.getMeta().data.splice(start, count); + this.insertElements(start, arguments.length - 2); + }, + + /** + * @private + */ + onDataUnshift: function() { + this.insertElements(0, arguments.length); + } + }); + + Chart.DatasetController.extend = helpers.inherits; +}; + +},{"45":45}],25:[function(require,module,exports){ +'use strict'; + +var helpers = require(45); + +module.exports = { + /** + * @private + */ + _set: function(scope, values) { + return helpers.merge(this[scope] || (this[scope] = {}), values); + } +}; + +},{"45":45}],26:[function(require,module,exports){ +'use strict'; + +var color = require(3); +var helpers = require(45); + +function interpolate(start, view, model, ease) { + var keys = Object.keys(model); + var i, ilen, key, actual, origin, target, type, c0, c1; + + for (i = 0, ilen = keys.length; i < ilen; ++i) { + key = keys[i]; + + target = model[key]; + + // if a value is added to the model after pivot() has been called, the view + // doesn't contain it, so let's initialize the view to the target value. + if (!view.hasOwnProperty(key)) { + view[key] = target; + } + + actual = view[key]; + + if (actual === target || key[0] === '_') { + continue; + } + + if (!start.hasOwnProperty(key)) { + start[key] = actual; + } + + origin = start[key]; + + type = typeof target; + + if (type === typeof origin) { + if (type === 'string') { + c0 = color(origin); + if (c0.valid) { + c1 = color(target); + if (c1.valid) { + view[key] = c1.mix(c0, ease).rgbString(); + continue; + } + } + } else if (type === 'number' && isFinite(origin) && isFinite(target)) { + view[key] = origin + (target - origin) * ease; + continue; + } + } + + view[key] = target; + } +} + +var Element = function(configuration) { + helpers.extend(this, configuration); + this.initialize.apply(this, arguments); +}; + +helpers.extend(Element.prototype, { + + initialize: function() { + this.hidden = false; + }, + + pivot: function() { + var me = this; + if (!me._view) { + me._view = helpers.clone(me._model); + } + me._start = {}; + return me; + }, + + transition: function(ease) { + var me = this; + var model = me._model; + var start = me._start; + var view = me._view; + + // No animation -> No Transition + if (!model || ease === 1) { + me._view = model; + me._start = null; + return me; + } + + if (!view) { + view = me._view = {}; + } + + if (!start) { + start = me._start = {}; + } + + interpolate(start, view, model, ease); + + return me; + }, + + tooltipPosition: function() { + return { + x: this._model.x, + y: this._model.y + }; + }, + + hasValue: function() { + return helpers.isNumber(this._model.x) && helpers.isNumber(this._model.y); + } +}); + +Element.extend = helpers.inherits; + +module.exports = Element; + +},{"3":3,"45":45}],27:[function(require,module,exports){ +/* global window: false */ +/* global document: false */ +'use strict'; + +var color = require(3); +var defaults = require(25); +var helpers = require(45); + +module.exports = function(Chart) { + + // -- Basic js utility methods + + helpers.configMerge = function(/* objects ... */) { + return helpers.merge(helpers.clone(arguments[0]), [].slice.call(arguments, 1), { + merger: function(key, target, source, options) { + var tval = target[key] || {}; + var sval = source[key]; + + if (key === 'scales') { + // scale config merging is complex. Add our own function here for that + target[key] = helpers.scaleMerge(tval, sval); + } else if (key === 'scale') { + // used in polar area & radar charts since there is only one scale + target[key] = helpers.merge(tval, [Chart.scaleService.getScaleDefaults(sval.type), sval]); + } else { + helpers._merger(key, target, source, options); + } + } + }); + }; + + helpers.scaleMerge = function(/* objects ... */) { + return helpers.merge(helpers.clone(arguments[0]), [].slice.call(arguments, 1), { + merger: function(key, target, source, options) { + if (key === 'xAxes' || key === 'yAxes') { + var slen = source[key].length; + var i, type, scale; + + if (!target[key]) { + target[key] = []; + } + + for (i = 0; i < slen; ++i) { + scale = source[key][i]; + type = helpers.valueOrDefault(scale.type, key === 'xAxes' ? 'category' : 'linear'); + + if (i >= target[key].length) { + target[key].push({}); + } + + if (!target[key][i].type || (scale.type && scale.type !== target[key][i].type)) { + // new/untyped scale or type changed: let's apply the new defaults + // then merge source scale to correctly overwrite the defaults. + helpers.merge(target[key][i], [Chart.scaleService.getScaleDefaults(type), scale]); + } else { + // scales type are the same + helpers.merge(target[key][i], scale); + } + } + } else { + helpers._merger(key, target, source, options); + } + } + }); + }; + + helpers.where = function(collection, filterCallback) { + if (helpers.isArray(collection) && Array.prototype.filter) { + return collection.filter(filterCallback); + } + var filtered = []; + + helpers.each(collection, function(item) { + if (filterCallback(item)) { + filtered.push(item); + } + }); + + return filtered; + }; + helpers.findIndex = Array.prototype.findIndex ? + function(array, callback, scope) { + return array.findIndex(callback, scope); + } : + function(array, callback, scope) { + scope = scope === undefined ? array : scope; + for (var i = 0, ilen = array.length; i < ilen; ++i) { + if (callback.call(scope, array[i], i, array)) { + return i; + } + } + return -1; + }; + helpers.findNextWhere = function(arrayToSearch, filterCallback, startIndex) { + // Default to start of the array + if (helpers.isNullOrUndef(startIndex)) { + startIndex = -1; + } + for (var i = startIndex + 1; i < arrayToSearch.length; i++) { + var currentItem = arrayToSearch[i]; + if (filterCallback(currentItem)) { + return currentItem; + } + } + }; + helpers.findPreviousWhere = function(arrayToSearch, filterCallback, startIndex) { + // Default to end of the array + if (helpers.isNullOrUndef(startIndex)) { + startIndex = arrayToSearch.length; + } + for (var i = startIndex - 1; i >= 0; i--) { + var currentItem = arrayToSearch[i]; + if (filterCallback(currentItem)) { + return currentItem; + } + } + }; + + // -- Math methods + helpers.isNumber = function(n) { + return !isNaN(parseFloat(n)) && isFinite(n); + }; + helpers.almostEquals = function(x, y, epsilon) { + return Math.abs(x - y) < epsilon; + }; + helpers.almostWhole = function(x, epsilon) { + var rounded = Math.round(x); + return (((rounded - epsilon) < x) && ((rounded + epsilon) > x)); + }; + helpers.max = function(array) { + return array.reduce(function(max, value) { + if (!isNaN(value)) { + return Math.max(max, value); + } + return max; + }, Number.NEGATIVE_INFINITY); + }; + helpers.min = function(array) { + return array.reduce(function(min, value) { + if (!isNaN(value)) { + return Math.min(min, value); + } + return min; + }, Number.POSITIVE_INFINITY); + }; + helpers.sign = Math.sign ? + function(x) { + return Math.sign(x); + } : + function(x) { + x = +x; // convert to a number + if (x === 0 || isNaN(x)) { + return x; + } + return x > 0 ? 1 : -1; + }; + helpers.log10 = Math.log10 ? + function(x) { + return Math.log10(x); + } : + function(x) { + return Math.log(x) / Math.LN10; + }; + helpers.toRadians = function(degrees) { + return degrees * (Math.PI / 180); + }; + helpers.toDegrees = function(radians) { + return radians * (180 / Math.PI); + }; + // Gets the angle from vertical upright to the point about a centre. + helpers.getAngleFromPoint = function(centrePoint, anglePoint) { + var distanceFromXCenter = anglePoint.x - centrePoint.x; + var distanceFromYCenter = anglePoint.y - centrePoint.y; + var radialDistanceFromCenter = Math.sqrt(distanceFromXCenter * distanceFromXCenter + distanceFromYCenter * distanceFromYCenter); + + var angle = Math.atan2(distanceFromYCenter, distanceFromXCenter); + + if (angle < (-0.5 * Math.PI)) { + angle += 2.0 * Math.PI; // make sure the returned angle is in the range of (-PI/2, 3PI/2] + } + + return { + angle: angle, + distance: radialDistanceFromCenter + }; + }; + helpers.distanceBetweenPoints = function(pt1, pt2) { + return Math.sqrt(Math.pow(pt2.x - pt1.x, 2) + Math.pow(pt2.y - pt1.y, 2)); + }; + helpers.aliasPixel = function(pixelWidth) { + return (pixelWidth % 2 === 0) ? 0 : 0.5; + }; + helpers.splineCurve = function(firstPoint, middlePoint, afterPoint, t) { + // Props to Rob Spencer at scaled innovation for his post on splining between points + // http://scaledinnovation.com/analytics/splines/aboutSplines.html + + // This function must also respect "skipped" points + + var previous = firstPoint.skip ? middlePoint : firstPoint; + var current = middlePoint; + var next = afterPoint.skip ? middlePoint : afterPoint; + + var d01 = Math.sqrt(Math.pow(current.x - previous.x, 2) + Math.pow(current.y - previous.y, 2)); + var d12 = Math.sqrt(Math.pow(next.x - current.x, 2) + Math.pow(next.y - current.y, 2)); + + var s01 = d01 / (d01 + d12); + var s12 = d12 / (d01 + d12); + + // If all points are the same, s01 & s02 will be inf + s01 = isNaN(s01) ? 0 : s01; + s12 = isNaN(s12) ? 0 : s12; + + var fa = t * s01; // scaling factor for triangle Ta + var fb = t * s12; + + return { + previous: { + x: current.x - fa * (next.x - previous.x), + y: current.y - fa * (next.y - previous.y) + }, + next: { + x: current.x + fb * (next.x - previous.x), + y: current.y + fb * (next.y - previous.y) + } + }; + }; + helpers.EPSILON = Number.EPSILON || 1e-14; + helpers.splineCurveMonotone = function(points) { + // This function calculates Bézier control points in a similar way than |splineCurve|, + // but preserves monotonicity of the provided data and ensures no local extremums are added + // between the dataset discrete points due to the interpolation. + // See : https://en.wikipedia.org/wiki/Monotone_cubic_interpolation + + var pointsWithTangents = (points || []).map(function(point) { + return { + model: point._model, + deltaK: 0, + mK: 0 + }; + }); + + // Calculate slopes (deltaK) and initialize tangents (mK) + var pointsLen = pointsWithTangents.length; + var i, pointBefore, pointCurrent, pointAfter; + for (i = 0; i < pointsLen; ++i) { + pointCurrent = pointsWithTangents[i]; + if (pointCurrent.model.skip) { + continue; + } + + pointBefore = i > 0 ? pointsWithTangents[i - 1] : null; + pointAfter = i < pointsLen - 1 ? pointsWithTangents[i + 1] : null; + if (pointAfter && !pointAfter.model.skip) { + var slopeDeltaX = (pointAfter.model.x - pointCurrent.model.x); + + // In the case of two points that appear at the same x pixel, slopeDeltaX is 0 + pointCurrent.deltaK = slopeDeltaX !== 0 ? (pointAfter.model.y - pointCurrent.model.y) / slopeDeltaX : 0; + } + + if (!pointBefore || pointBefore.model.skip) { + pointCurrent.mK = pointCurrent.deltaK; + } else if (!pointAfter || pointAfter.model.skip) { + pointCurrent.mK = pointBefore.deltaK; + } else if (this.sign(pointBefore.deltaK) !== this.sign(pointCurrent.deltaK)) { + pointCurrent.mK = 0; + } else { + pointCurrent.mK = (pointBefore.deltaK + pointCurrent.deltaK) / 2; + } + } + + // Adjust tangents to ensure monotonic properties + var alphaK, betaK, tauK, squaredMagnitude; + for (i = 0; i < pointsLen - 1; ++i) { + pointCurrent = pointsWithTangents[i]; + pointAfter = pointsWithTangents[i + 1]; + if (pointCurrent.model.skip || pointAfter.model.skip) { + continue; + } + + if (helpers.almostEquals(pointCurrent.deltaK, 0, this.EPSILON)) { + pointCurrent.mK = pointAfter.mK = 0; + continue; + } + + alphaK = pointCurrent.mK / pointCurrent.deltaK; + betaK = pointAfter.mK / pointCurrent.deltaK; + squaredMagnitude = Math.pow(alphaK, 2) + Math.pow(betaK, 2); + if (squaredMagnitude <= 9) { + continue; + } + + tauK = 3 / Math.sqrt(squaredMagnitude); + pointCurrent.mK = alphaK * tauK * pointCurrent.deltaK; + pointAfter.mK = betaK * tauK * pointCurrent.deltaK; + } + + // Compute control points + var deltaX; + for (i = 0; i < pointsLen; ++i) { + pointCurrent = pointsWithTangents[i]; + if (pointCurrent.model.skip) { + continue; + } + + pointBefore = i > 0 ? pointsWithTangents[i - 1] : null; + pointAfter = i < pointsLen - 1 ? pointsWithTangents[i + 1] : null; + if (pointBefore && !pointBefore.model.skip) { + deltaX = (pointCurrent.model.x - pointBefore.model.x) / 3; + pointCurrent.model.controlPointPreviousX = pointCurrent.model.x - deltaX; + pointCurrent.model.controlPointPreviousY = pointCurrent.model.y - deltaX * pointCurrent.mK; + } + if (pointAfter && !pointAfter.model.skip) { + deltaX = (pointAfter.model.x - pointCurrent.model.x) / 3; + pointCurrent.model.controlPointNextX = pointCurrent.model.x + deltaX; + pointCurrent.model.controlPointNextY = pointCurrent.model.y + deltaX * pointCurrent.mK; + } + } + }; + helpers.nextItem = function(collection, index, loop) { + if (loop) { + return index >= collection.length - 1 ? collection[0] : collection[index + 1]; + } + return index >= collection.length - 1 ? collection[collection.length - 1] : collection[index + 1]; + }; + helpers.previousItem = function(collection, index, loop) { + if (loop) { + return index <= 0 ? collection[collection.length - 1] : collection[index - 1]; + } + return index <= 0 ? collection[0] : collection[index - 1]; + }; + // Implementation of the nice number algorithm used in determining where axis labels will go + helpers.niceNum = function(range, round) { + var exponent = Math.floor(helpers.log10(range)); + var fraction = range / Math.pow(10, exponent); + var niceFraction; + + if (round) { + if (fraction < 1.5) { + niceFraction = 1; + } else if (fraction < 3) { + niceFraction = 2; + } else if (fraction < 7) { + niceFraction = 5; + } else { + niceFraction = 10; + } + } else if (fraction <= 1.0) { + niceFraction = 1; + } else if (fraction <= 2) { + niceFraction = 2; + } else if (fraction <= 5) { + niceFraction = 5; + } else { + niceFraction = 10; + } + + return niceFraction * Math.pow(10, exponent); + }; + // Request animation polyfill - http://www.paulirish.com/2011/requestanimationframe-for-smart-animating/ + helpers.requestAnimFrame = (function() { + if (typeof window === 'undefined') { + return function(callback) { + callback(); + }; + } + return window.requestAnimationFrame || + window.webkitRequestAnimationFrame || + window.mozRequestAnimationFrame || + window.oRequestAnimationFrame || + window.msRequestAnimationFrame || + function(callback) { + return window.setTimeout(callback, 1000 / 60); + }; + }()); + // -- DOM methods + helpers.getRelativePosition = function(evt, chart) { + var mouseX, mouseY; + var e = evt.originalEvent || evt; + var canvas = evt.currentTarget || evt.srcElement; + var boundingRect = canvas.getBoundingClientRect(); + + var touches = e.touches; + if (touches && touches.length > 0) { + mouseX = touches[0].clientX; + mouseY = touches[0].clientY; + + } else { + mouseX = e.clientX; + mouseY = e.clientY; + } + + // Scale mouse coordinates into canvas coordinates + // by following the pattern laid out by 'jerryj' in the comments of + // http://www.html5canvastutorials.com/advanced/html5-canvas-mouse-coordinates/ + var paddingLeft = parseFloat(helpers.getStyle(canvas, 'padding-left')); + var paddingTop = parseFloat(helpers.getStyle(canvas, 'padding-top')); + var paddingRight = parseFloat(helpers.getStyle(canvas, 'padding-right')); + var paddingBottom = parseFloat(helpers.getStyle(canvas, 'padding-bottom')); + var width = boundingRect.right - boundingRect.left - paddingLeft - paddingRight; + var height = boundingRect.bottom - boundingRect.top - paddingTop - paddingBottom; + + // We divide by the current device pixel ratio, because the canvas is scaled up by that amount in each direction. However + // the backend model is in unscaled coordinates. Since we are going to deal with our model coordinates, we go back here + mouseX = Math.round((mouseX - boundingRect.left - paddingLeft) / (width) * canvas.width / chart.currentDevicePixelRatio); + mouseY = Math.round((mouseY - boundingRect.top - paddingTop) / (height) * canvas.height / chart.currentDevicePixelRatio); + + return { + x: mouseX, + y: mouseY + }; + + }; + + // Private helper function to convert max-width/max-height values that may be percentages into a number + function parseMaxStyle(styleValue, node, parentProperty) { + var valueInPixels; + if (typeof styleValue === 'string') { + valueInPixels = parseInt(styleValue, 10); + + if (styleValue.indexOf('%') !== -1) { + // percentage * size in dimension + valueInPixels = valueInPixels / 100 * node.parentNode[parentProperty]; + } + } else { + valueInPixels = styleValue; + } + + return valueInPixels; + } + + /** + * Returns if the given value contains an effective constraint. + * @private + */ + function isConstrainedValue(value) { + return value !== undefined && value !== null && value !== 'none'; + } + + // Private helper to get a constraint dimension + // @param domNode : the node to check the constraint on + // @param maxStyle : the style that defines the maximum for the direction we are using (maxWidth / maxHeight) + // @param percentageProperty : property of parent to use when calculating width as a percentage + // @see http://www.nathanaeljones.com/blog/2013/reading-max-width-cross-browser + function getConstraintDimension(domNode, maxStyle, percentageProperty) { + var view = document.defaultView; + var parentNode = domNode.parentNode; + var constrainedNode = view.getComputedStyle(domNode)[maxStyle]; + var constrainedContainer = view.getComputedStyle(parentNode)[maxStyle]; + var hasCNode = isConstrainedValue(constrainedNode); + var hasCContainer = isConstrainedValue(constrainedContainer); + var infinity = Number.POSITIVE_INFINITY; + + if (hasCNode || hasCContainer) { + return Math.min( + hasCNode ? parseMaxStyle(constrainedNode, domNode, percentageProperty) : infinity, + hasCContainer ? parseMaxStyle(constrainedContainer, parentNode, percentageProperty) : infinity); + } + + return 'none'; + } + // returns Number or undefined if no constraint + helpers.getConstraintWidth = function(domNode) { + return getConstraintDimension(domNode, 'max-width', 'clientWidth'); + }; + // returns Number or undefined if no constraint + helpers.getConstraintHeight = function(domNode) { + return getConstraintDimension(domNode, 'max-height', 'clientHeight'); + }; + helpers.getMaximumWidth = function(domNode) { + var container = domNode.parentNode; + if (!container) { + return domNode.clientWidth; + } + + var paddingLeft = parseInt(helpers.getStyle(container, 'padding-left'), 10); + var paddingRight = parseInt(helpers.getStyle(container, 'padding-right'), 10); + var w = container.clientWidth - paddingLeft - paddingRight; + var cw = helpers.getConstraintWidth(domNode); + return isNaN(cw) ? w : Math.min(w, cw); + }; + helpers.getMaximumHeight = function(domNode) { + var container = domNode.parentNode; + if (!container) { + return domNode.clientHeight; + } + + var paddingTop = parseInt(helpers.getStyle(container, 'padding-top'), 10); + var paddingBottom = parseInt(helpers.getStyle(container, 'padding-bottom'), 10); + var h = container.clientHeight - paddingTop - paddingBottom; + var ch = helpers.getConstraintHeight(domNode); + return isNaN(ch) ? h : Math.min(h, ch); + }; + helpers.getStyle = function(el, property) { + return el.currentStyle ? + el.currentStyle[property] : + document.defaultView.getComputedStyle(el, null).getPropertyValue(property); + }; + helpers.retinaScale = function(chart, forceRatio) { + var pixelRatio = chart.currentDevicePixelRatio = forceRatio || window.devicePixelRatio || 1; + if (pixelRatio === 1) { + return; + } + + var canvas = chart.canvas; + var height = chart.height; + var width = chart.width; + + canvas.height = height * pixelRatio; + canvas.width = width * pixelRatio; + chart.ctx.scale(pixelRatio, pixelRatio); + + // If no style has been set on the canvas, the render size is used as display size, + // making the chart visually bigger, so let's enforce it to the "correct" values. + // See https://github.com/chartjs/Chart.js/issues/3575 + canvas.style.height = height + 'px'; + canvas.style.width = width + 'px'; + }; + // -- Canvas methods + helpers.fontString = function(pixelSize, fontStyle, fontFamily) { + return fontStyle + ' ' + pixelSize + 'px ' + fontFamily; + }; + helpers.longestText = function(ctx, font, arrayOfThings, cache) { + cache = cache || {}; + var data = cache.data = cache.data || {}; + var gc = cache.garbageCollect = cache.garbageCollect || []; + + if (cache.font !== font) { + data = cache.data = {}; + gc = cache.garbageCollect = []; + cache.font = font; + } + + ctx.font = font; + var longest = 0; + helpers.each(arrayOfThings, function(thing) { + // Undefined strings and arrays should not be measured + if (thing !== undefined && thing !== null && helpers.isArray(thing) !== true) { + longest = helpers.measureText(ctx, data, gc, longest, thing); + } else if (helpers.isArray(thing)) { + // if it is an array lets measure each element + // to do maybe simplify this function a bit so we can do this more recursively? + helpers.each(thing, function(nestedThing) { + // Undefined strings and arrays should not be measured + if (nestedThing !== undefined && nestedThing !== null && !helpers.isArray(nestedThing)) { + longest = helpers.measureText(ctx, data, gc, longest, nestedThing); + } + }); + } + }); + + var gcLen = gc.length / 2; + if (gcLen > arrayOfThings.length) { + for (var i = 0; i < gcLen; i++) { + delete data[gc[i]]; + } + gc.splice(0, gcLen); + } + return longest; + }; + helpers.measureText = function(ctx, data, gc, longest, string) { + var textWidth = data[string]; + if (!textWidth) { + textWidth = data[string] = ctx.measureText(string).width; + gc.push(string); + } + if (textWidth > longest) { + longest = textWidth; + } + return longest; + }; + helpers.numberOfLabelLines = function(arrayOfThings) { + var numberOfLines = 1; + helpers.each(arrayOfThings, function(thing) { + if (helpers.isArray(thing)) { + if (thing.length > numberOfLines) { + numberOfLines = thing.length; + } + } + }); + return numberOfLines; + }; + + helpers.color = !color ? + function(value) { + console.error('Color.js not found!'); + return value; + } : + function(value) { + /* global CanvasGradient */ + if (value instanceof CanvasGradient) { + value = defaults.global.defaultColor; + } + + return color(value); + }; + + helpers.getHoverColor = function(colorValue) { + /* global CanvasPattern */ + return (colorValue instanceof CanvasPattern) ? + colorValue : + helpers.color(colorValue).saturate(0.5).darken(0.1).rgbString(); + }; +}; + +},{"25":25,"3":3,"45":45}],28:[function(require,module,exports){ +'use strict'; + +var helpers = require(45); + +/** + * Helper function to get relative position for an event + * @param {Event|IEvent} event - The event to get the position for + * @param {Chart} chart - The chart + * @returns {Point} the event position + */ +function getRelativePosition(e, chart) { + if (e.native) { + return { + x: e.x, + y: e.y + }; + } + + return helpers.getRelativePosition(e, chart); +} + +/** + * Helper function to traverse all of the visible elements in the chart + * @param chart {chart} the chart + * @param handler {Function} the callback to execute for each visible item + */ +function parseVisibleItems(chart, handler) { + var datasets = chart.data.datasets; + var meta, i, j, ilen, jlen; + + for (i = 0, ilen = datasets.length; i < ilen; ++i) { + if (!chart.isDatasetVisible(i)) { + continue; + } + + meta = chart.getDatasetMeta(i); + for (j = 0, jlen = meta.data.length; j < jlen; ++j) { + var element = meta.data[j]; + if (!element._view.skip) { + handler(element); + } + } + } +} + +/** + * Helper function to get the items that intersect the event position + * @param items {ChartElement[]} elements to filter + * @param position {Point} the point to be nearest to + * @return {ChartElement[]} the nearest items + */ +function getIntersectItems(chart, position) { + var elements = []; + + parseVisibleItems(chart, function(element) { + if (element.inRange(position.x, position.y)) { + elements.push(element); + } + }); + + return elements; +} + +/** + * Helper function to get the items nearest to the event position considering all visible items in teh chart + * @param chart {Chart} the chart to look at elements from + * @param position {Point} the point to be nearest to + * @param intersect {Boolean} if true, only consider items that intersect the position + * @param distanceMetric {Function} function to provide the distance between points + * @return {ChartElement[]} the nearest items + */ +function getNearestItems(chart, position, intersect, distanceMetric) { + var minDistance = Number.POSITIVE_INFINITY; + var nearestItems = []; + + parseVisibleItems(chart, function(element) { + if (intersect && !element.inRange(position.x, position.y)) { + return; + } + + var center = element.getCenterPoint(); + var distance = distanceMetric(position, center); + + if (distance < minDistance) { + nearestItems = [element]; + minDistance = distance; + } else if (distance === minDistance) { + // Can have multiple items at the same distance in which case we sort by size + nearestItems.push(element); + } + }); + + return nearestItems; +} + +/** + * Get a distance metric function for two points based on the + * axis mode setting + * @param {String} axis the axis mode. x|y|xy + */ +function getDistanceMetricForAxis(axis) { + var useX = axis.indexOf('x') !== -1; + var useY = axis.indexOf('y') !== -1; + + return function(pt1, pt2) { + var deltaX = useX ? Math.abs(pt1.x - pt2.x) : 0; + var deltaY = useY ? Math.abs(pt1.y - pt2.y) : 0; + return Math.sqrt(Math.pow(deltaX, 2) + Math.pow(deltaY, 2)); + }; +} + +function indexMode(chart, e, options) { + var position = getRelativePosition(e, chart); + // Default axis for index mode is 'x' to match old behaviour + options.axis = options.axis || 'x'; + var distanceMetric = getDistanceMetricForAxis(options.axis); + var items = options.intersect ? getIntersectItems(chart, position) : getNearestItems(chart, position, false, distanceMetric); + var elements = []; + + if (!items.length) { + return []; + } + + chart.data.datasets.forEach(function(dataset, datasetIndex) { + if (chart.isDatasetVisible(datasetIndex)) { + var meta = chart.getDatasetMeta(datasetIndex); + var element = meta.data[items[0]._index]; + + // don't count items that are skipped (null data) + if (element && !element._view.skip) { + elements.push(element); + } + } + }); + + return elements; +} + +/** + * @interface IInteractionOptions + */ +/** + * If true, only consider items that intersect the point + * @name IInterfaceOptions#boolean + * @type Boolean + */ + +/** + * Contains interaction related functions + * @namespace Chart.Interaction + */ +module.exports = { + // Helper function for different modes + modes: { + single: function(chart, e) { + var position = getRelativePosition(e, chart); + var elements = []; + + parseVisibleItems(chart, function(element) { + if (element.inRange(position.x, position.y)) { + elements.push(element); + return elements; + } + }); + + return elements.slice(0, 1); + }, + + /** + * @function Chart.Interaction.modes.label + * @deprecated since version 2.4.0 + * @todo remove at version 3 + * @private + */ + label: indexMode, + + /** + * Returns items at the same index. If the options.intersect parameter is true, we only return items if we intersect something + * If the options.intersect mode is false, we find the nearest item and return the items at the same index as that item + * @function Chart.Interaction.modes.index + * @since v2.4.0 + * @param chart {chart} the chart we are returning items from + * @param e {Event} the event we are find things at + * @param options {IInteractionOptions} options to use during interaction + * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned + */ + index: indexMode, + + /** + * Returns items in the same dataset. If the options.intersect parameter is true, we only return items if we intersect something + * If the options.intersect is false, we find the nearest item and return the items in that dataset + * @function Chart.Interaction.modes.dataset + * @param chart {chart} the chart we are returning items from + * @param e {Event} the event we are find things at + * @param options {IInteractionOptions} options to use during interaction + * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned + */ + dataset: function(chart, e, options) { + var position = getRelativePosition(e, chart); + options.axis = options.axis || 'xy'; + var distanceMetric = getDistanceMetricForAxis(options.axis); + var items = options.intersect ? getIntersectItems(chart, position) : getNearestItems(chart, position, false, distanceMetric); + + if (items.length > 0) { + items = chart.getDatasetMeta(items[0]._datasetIndex).data; + } + + return items; + }, + + /** + * @function Chart.Interaction.modes.x-axis + * @deprecated since version 2.4.0. Use index mode and intersect == true + * @todo remove at version 3 + * @private + */ + 'x-axis': function(chart, e) { + return indexMode(chart, e, {intersect: false}); + }, + + /** + * Point mode returns all elements that hit test based on the event position + * of the event + * @function Chart.Interaction.modes.intersect + * @param chart {chart} the chart we are returning items from + * @param e {Event} the event we are find things at + * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned + */ + point: function(chart, e) { + var position = getRelativePosition(e, chart); + return getIntersectItems(chart, position); + }, + + /** + * nearest mode returns the element closest to the point + * @function Chart.Interaction.modes.intersect + * @param chart {chart} the chart we are returning items from + * @param e {Event} the event we are find things at + * @param options {IInteractionOptions} options to use + * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned + */ + nearest: function(chart, e, options) { + var position = getRelativePosition(e, chart); + options.axis = options.axis || 'xy'; + var distanceMetric = getDistanceMetricForAxis(options.axis); + var nearestItems = getNearestItems(chart, position, options.intersect, distanceMetric); + + // We have multiple items at the same distance from the event. Now sort by smallest + if (nearestItems.length > 1) { + nearestItems.sort(function(a, b) { + var sizeA = a.getArea(); + var sizeB = b.getArea(); + var ret = sizeA - sizeB; + + if (ret === 0) { + // if equal sort by dataset index + ret = a._datasetIndex - b._datasetIndex; + } + + return ret; + }); + } + + // Return only 1 item + return nearestItems.slice(0, 1); + }, + + /** + * x mode returns the elements that hit-test at the current x coordinate + * @function Chart.Interaction.modes.x + * @param chart {chart} the chart we are returning items from + * @param e {Event} the event we are find things at + * @param options {IInteractionOptions} options to use + * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned + */ + x: function(chart, e, options) { + var position = getRelativePosition(e, chart); + var items = []; + var intersectsItem = false; + + parseVisibleItems(chart, function(element) { + if (element.inXRange(position.x)) { + items.push(element); + } + + if (element.inRange(position.x, position.y)) { + intersectsItem = true; + } + }); + + // If we want to trigger on an intersect and we don't have any items + // that intersect the position, return nothing + if (options.intersect && !intersectsItem) { + items = []; + } + return items; + }, + + /** + * y mode returns the elements that hit-test at the current y coordinate + * @function Chart.Interaction.modes.y + * @param chart {chart} the chart we are returning items from + * @param e {Event} the event we are find things at + * @param options {IInteractionOptions} options to use + * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned + */ + y: function(chart, e, options) { + var position = getRelativePosition(e, chart); + var items = []; + var intersectsItem = false; + + parseVisibleItems(chart, function(element) { + if (element.inYRange(position.y)) { + items.push(element); + } + + if (element.inRange(position.x, position.y)) { + intersectsItem = true; + } + }); + + // If we want to trigger on an intersect and we don't have any items + // that intersect the position, return nothing + if (options.intersect && !intersectsItem) { + items = []; + } + return items; + } + } +}; + +},{"45":45}],29:[function(require,module,exports){ +'use strict'; + +var defaults = require(25); + +defaults._set('global', { + responsive: true, + responsiveAnimationDuration: 0, + maintainAspectRatio: true, + events: ['mousemove', 'mouseout', 'click', 'touchstart', 'touchmove'], + hover: { + onHover: null, + mode: 'nearest', + intersect: true, + animationDuration: 400 + }, + onClick: null, + defaultColor: 'rgba(0,0,0,0.1)', + defaultFontColor: '#666', + defaultFontFamily: "'Helvetica Neue', 'Helvetica', 'Arial', sans-serif", + defaultFontSize: 12, + defaultFontStyle: 'normal', + showLines: true, + + // Element defaults defined in element extensions + elements: {}, + + // Layout options such as padding + layout: { + padding: { + top: 0, + right: 0, + bottom: 0, + left: 0 + } + } +}); + +module.exports = function() { + + // Occupy the global variable of Chart, and create a simple base class + var Chart = function(item, config) { + this.construct(item, config); + return this; + }; + + Chart.Chart = Chart; + + return Chart; +}; + +},{"25":25}],30:[function(require,module,exports){ +'use strict'; + +var helpers = require(45); + +module.exports = function(Chart) { + + function filterByPosition(array, position) { + return helpers.where(array, function(v) { + return v.position === position; + }); + } + + function sortByWeight(array, reverse) { + array.forEach(function(v, i) { + v._tmpIndex_ = i; + return v; + }); + array.sort(function(a, b) { + var v0 = reverse ? b : a; + var v1 = reverse ? a : b; + return v0.weight === v1.weight ? + v0._tmpIndex_ - v1._tmpIndex_ : + v0.weight - v1.weight; + }); + array.forEach(function(v) { + delete v._tmpIndex_; + }); + } + + /** + * @interface ILayoutItem + * @prop {String} position - The position of the item in the chart layout. Possible values are + * 'left', 'top', 'right', 'bottom', and 'chartArea' + * @prop {Number} weight - The weight used to sort the item. Higher weights are further away from the chart area + * @prop {Boolean} fullWidth - if true, and the item is horizontal, then push vertical boxes down + * @prop {Function} isHorizontal - returns true if the layout item is horizontal (ie. top or bottom) + * @prop {Function} update - Takes two parameters: width and height. Returns size of item + * @prop {Function} getPadding - Returns an object with padding on the edges + * @prop {Number} width - Width of item. Must be valid after update() + * @prop {Number} height - Height of item. Must be valid after update() + * @prop {Number} left - Left edge of the item. Set by layout system and cannot be used in update + * @prop {Number} top - Top edge of the item. Set by layout system and cannot be used in update + * @prop {Number} right - Right edge of the item. Set by layout system and cannot be used in update + * @prop {Number} bottom - Bottom edge of the item. Set by layout system and cannot be used in update + */ + + // The layout service is very self explanatory. It's responsible for the layout within a chart. + // Scales, Legends and Plugins all rely on the layout service and can easily register to be placed anywhere they need + // It is this service's responsibility of carrying out that layout. + Chart.layoutService = { + defaults: {}, + + /** + * Register a box to a chart. + * A box is simply a reference to an object that requires layout. eg. Scales, Legend, Title. + * @param {Chart} chart - the chart to use + * @param {ILayoutItem} item - the item to add to be layed out + */ + addBox: function(chart, item) { + if (!chart.boxes) { + chart.boxes = []; + } + + // initialize item with default values + item.fullWidth = item.fullWidth || false; + item.position = item.position || 'top'; + item.weight = item.weight || 0; + + chart.boxes.push(item); + }, + + /** + * Remove a layoutItem from a chart + * @param {Chart} chart - the chart to remove the box from + * @param {Object} layoutItem - the item to remove from the layout + */ + removeBox: function(chart, layoutItem) { + var index = chart.boxes ? chart.boxes.indexOf(layoutItem) : -1; + if (index !== -1) { + chart.boxes.splice(index, 1); + } + }, + + /** + * Sets (or updates) options on the given `item`. + * @param {Chart} chart - the chart in which the item lives (or will be added to) + * @param {Object} item - the item to configure with the given options + * @param {Object} options - the new item options. + */ + configure: function(chart, item, options) { + var props = ['fullWidth', 'position', 'weight']; + var ilen = props.length; + var i = 0; + var prop; + + for (; i < ilen; ++i) { + prop = props[i]; + if (options.hasOwnProperty(prop)) { + item[prop] = options[prop]; + } + } + }, + + /** + * Fits boxes of the given chart into the given size by having each box measure itself + * then running a fitting algorithm + * @param {Chart} chart - the chart + * @param {Number} width - the width to fit into + * @param {Number} height - the height to fit into + */ + update: function(chart, width, height) { + if (!chart) { + return; + } + + var layoutOptions = chart.options.layout || {}; + var padding = helpers.options.toPadding(layoutOptions.padding); + var leftPadding = padding.left; + var rightPadding = padding.right; + var topPadding = padding.top; + var bottomPadding = padding.bottom; + + var leftBoxes = filterByPosition(chart.boxes, 'left'); + var rightBoxes = filterByPosition(chart.boxes, 'right'); + var topBoxes = filterByPosition(chart.boxes, 'top'); + var bottomBoxes = filterByPosition(chart.boxes, 'bottom'); + var chartAreaBoxes = filterByPosition(chart.boxes, 'chartArea'); + + // Sort boxes by weight. A higher weight is further away from the chart area + sortByWeight(leftBoxes, true); + sortByWeight(rightBoxes, false); + sortByWeight(topBoxes, true); + sortByWeight(bottomBoxes, false); + + // Essentially we now have any number of boxes on each of the 4 sides. + // Our canvas looks like the following. + // The areas L1 and L2 are the left axes. R1 is the right axis, T1 is the top axis and + // B1 is the bottom axis + // There are also 4 quadrant-like locations (left to right instead of clockwise) reserved for chart overlays + // These locations are single-box locations only, when trying to register a chartArea location that is already taken, + // an error will be thrown. + // + // |----------------------------------------------------| + // | T1 (Full Width) | + // |----------------------------------------------------| + // | | | T2 | | + // | |----|-------------------------------------|----| + // | | | C1 | | C2 | | + // | | |----| |----| | + // | | | | | + // | L1 | L2 | ChartArea (C0) | R1 | + // | | | | | + // | | |----| |----| | + // | | | C3 | | C4 | | + // | |----|-------------------------------------|----| + // | | | B1 | | + // |----------------------------------------------------| + // | B2 (Full Width) | + // |----------------------------------------------------| + // + // What we do to find the best sizing, we do the following + // 1. Determine the minimum size of the chart area. + // 2. Split the remaining width equally between each vertical axis + // 3. Split the remaining height equally between each horizontal axis + // 4. Give each layout the maximum size it can be. The layout will return it's minimum size + // 5. Adjust the sizes of each axis based on it's minimum reported size. + // 6. Refit each axis + // 7. Position each axis in the final location + // 8. Tell the chart the final location of the chart area + // 9. Tell any axes that overlay the chart area the positions of the chart area + + // Step 1 + var chartWidth = width - leftPadding - rightPadding; + var chartHeight = height - topPadding - bottomPadding; + var chartAreaWidth = chartWidth / 2; // min 50% + var chartAreaHeight = chartHeight / 2; // min 50% + + // Step 2 + var verticalBoxWidth = (width - chartAreaWidth) / (leftBoxes.length + rightBoxes.length); + + // Step 3 + var horizontalBoxHeight = (height - chartAreaHeight) / (topBoxes.length + bottomBoxes.length); + + // Step 4 + var maxChartAreaWidth = chartWidth; + var maxChartAreaHeight = chartHeight; + var minBoxSizes = []; + + function getMinimumBoxSize(box) { + var minSize; + var isHorizontal = box.isHorizontal(); + + if (isHorizontal) { + minSize = box.update(box.fullWidth ? chartWidth : maxChartAreaWidth, horizontalBoxHeight); + maxChartAreaHeight -= minSize.height; + } else { + minSize = box.update(verticalBoxWidth, chartAreaHeight); + maxChartAreaWidth -= minSize.width; + } + + minBoxSizes.push({ + horizontal: isHorizontal, + minSize: minSize, + box: box, + }); + } + + helpers.each(leftBoxes.concat(rightBoxes, topBoxes, bottomBoxes), getMinimumBoxSize); + + // If a horizontal box has padding, we move the left boxes over to avoid ugly charts (see issue #2478) + var maxHorizontalLeftPadding = 0; + var maxHorizontalRightPadding = 0; + var maxVerticalTopPadding = 0; + var maxVerticalBottomPadding = 0; + + helpers.each(topBoxes.concat(bottomBoxes), function(horizontalBox) { + if (horizontalBox.getPadding) { + var boxPadding = horizontalBox.getPadding(); + maxHorizontalLeftPadding = Math.max(maxHorizontalLeftPadding, boxPadding.left); + maxHorizontalRightPadding = Math.max(maxHorizontalRightPadding, boxPadding.right); + } + }); + + helpers.each(leftBoxes.concat(rightBoxes), function(verticalBox) { + if (verticalBox.getPadding) { + var boxPadding = verticalBox.getPadding(); + maxVerticalTopPadding = Math.max(maxVerticalTopPadding, boxPadding.top); + maxVerticalBottomPadding = Math.max(maxVerticalBottomPadding, boxPadding.bottom); + } + }); + + // At this point, maxChartAreaHeight and maxChartAreaWidth are the size the chart area could + // be if the axes are drawn at their minimum sizes. + // Steps 5 & 6 + var totalLeftBoxesWidth = leftPadding; + var totalRightBoxesWidth = rightPadding; + var totalTopBoxesHeight = topPadding; + var totalBottomBoxesHeight = bottomPadding; + + // Function to fit a box + function fitBox(box) { + var minBoxSize = helpers.findNextWhere(minBoxSizes, function(minBox) { + return minBox.box === box; + }); + + if (minBoxSize) { + if (box.isHorizontal()) { + var scaleMargin = { + left: Math.max(totalLeftBoxesWidth, maxHorizontalLeftPadding), + right: Math.max(totalRightBoxesWidth, maxHorizontalRightPadding), + top: 0, + bottom: 0 + }; + + // Don't use min size here because of label rotation. When the labels are rotated, their rotation highly depends + // on the margin. Sometimes they need to increase in size slightly + box.update(box.fullWidth ? chartWidth : maxChartAreaWidth, chartHeight / 2, scaleMargin); + } else { + box.update(minBoxSize.minSize.width, maxChartAreaHeight); + } + } + } + + // Update, and calculate the left and right margins for the horizontal boxes + helpers.each(leftBoxes.concat(rightBoxes), fitBox); + + helpers.each(leftBoxes, function(box) { + totalLeftBoxesWidth += box.width; + }); + + helpers.each(rightBoxes, function(box) { + totalRightBoxesWidth += box.width; + }); + + // Set the Left and Right margins for the horizontal boxes + helpers.each(topBoxes.concat(bottomBoxes), fitBox); + + // Figure out how much margin is on the top and bottom of the vertical boxes + helpers.each(topBoxes, function(box) { + totalTopBoxesHeight += box.height; + }); + + helpers.each(bottomBoxes, function(box) { + totalBottomBoxesHeight += box.height; + }); + + function finalFitVerticalBox(box) { + var minBoxSize = helpers.findNextWhere(minBoxSizes, function(minSize) { + return minSize.box === box; + }); + + var scaleMargin = { + left: 0, + right: 0, + top: totalTopBoxesHeight, + bottom: totalBottomBoxesHeight + }; + + if (minBoxSize) { + box.update(minBoxSize.minSize.width, maxChartAreaHeight, scaleMargin); + } + } + + // Let the left layout know the final margin + helpers.each(leftBoxes.concat(rightBoxes), finalFitVerticalBox); + + // Recalculate because the size of each layout might have changed slightly due to the margins (label rotation for instance) + totalLeftBoxesWidth = leftPadding; + totalRightBoxesWidth = rightPadding; + totalTopBoxesHeight = topPadding; + totalBottomBoxesHeight = bottomPadding; + + helpers.each(leftBoxes, function(box) { + totalLeftBoxesWidth += box.width; + }); + + helpers.each(rightBoxes, function(box) { + totalRightBoxesWidth += box.width; + }); + + helpers.each(topBoxes, function(box) { + totalTopBoxesHeight += box.height; + }); + helpers.each(bottomBoxes, function(box) { + totalBottomBoxesHeight += box.height; + }); + + // We may be adding some padding to account for rotated x axis labels + var leftPaddingAddition = Math.max(maxHorizontalLeftPadding - totalLeftBoxesWidth, 0); + totalLeftBoxesWidth += leftPaddingAddition; + totalRightBoxesWidth += Math.max(maxHorizontalRightPadding - totalRightBoxesWidth, 0); + + var topPaddingAddition = Math.max(maxVerticalTopPadding - totalTopBoxesHeight, 0); + totalTopBoxesHeight += topPaddingAddition; + totalBottomBoxesHeight += Math.max(maxVerticalBottomPadding - totalBottomBoxesHeight, 0); + + // Figure out if our chart area changed. This would occur if the dataset layout label rotation + // changed due to the application of the margins in step 6. Since we can only get bigger, this is safe to do + // without calling `fit` again + var newMaxChartAreaHeight = height - totalTopBoxesHeight - totalBottomBoxesHeight; + var newMaxChartAreaWidth = width - totalLeftBoxesWidth - totalRightBoxesWidth; + + if (newMaxChartAreaWidth !== maxChartAreaWidth || newMaxChartAreaHeight !== maxChartAreaHeight) { + helpers.each(leftBoxes, function(box) { + box.height = newMaxChartAreaHeight; + }); + + helpers.each(rightBoxes, function(box) { + box.height = newMaxChartAreaHeight; + }); + + helpers.each(topBoxes, function(box) { + if (!box.fullWidth) { + box.width = newMaxChartAreaWidth; + } + }); + + helpers.each(bottomBoxes, function(box) { + if (!box.fullWidth) { + box.width = newMaxChartAreaWidth; + } + }); + + maxChartAreaHeight = newMaxChartAreaHeight; + maxChartAreaWidth = newMaxChartAreaWidth; + } + + // Step 7 - Position the boxes + var left = leftPadding + leftPaddingAddition; + var top = topPadding + topPaddingAddition; + + function placeBox(box) { + if (box.isHorizontal()) { + box.left = box.fullWidth ? leftPadding : totalLeftBoxesWidth; + box.right = box.fullWidth ? width - rightPadding : totalLeftBoxesWidth + maxChartAreaWidth; + box.top = top; + box.bottom = top + box.height; + + // Move to next point + top = box.bottom; + + } else { + + box.left = left; + box.right = left + box.width; + box.top = totalTopBoxesHeight; + box.bottom = totalTopBoxesHeight + maxChartAreaHeight; + + // Move to next point + left = box.right; + } + } + + helpers.each(leftBoxes.concat(topBoxes), placeBox); + + // Account for chart width and height + left += maxChartAreaWidth; + top += maxChartAreaHeight; + + helpers.each(rightBoxes, placeBox); + helpers.each(bottomBoxes, placeBox); + + // Step 8 + chart.chartArea = { + left: totalLeftBoxesWidth, + top: totalTopBoxesHeight, + right: totalLeftBoxesWidth + maxChartAreaWidth, + bottom: totalTopBoxesHeight + maxChartAreaHeight + }; + + // Step 9 + helpers.each(chartAreaBoxes, function(box) { + box.left = chart.chartArea.left; + box.top = chart.chartArea.top; + box.right = chart.chartArea.right; + box.bottom = chart.chartArea.bottom; + + box.update(maxChartAreaWidth, maxChartAreaHeight); + }); + } + }; +}; + +},{"45":45}],31:[function(require,module,exports){ +'use strict'; + +var defaults = require(25); +var Element = require(26); +var helpers = require(45); + +defaults._set('global', { + plugins: {} +}); + +module.exports = function(Chart) { + + /** + * The plugin service singleton + * @namespace Chart.plugins + * @since 2.1.0 + */ + Chart.plugins = { + /** + * Globally registered plugins. + * @private + */ + _plugins: [], + + /** + * This identifier is used to invalidate the descriptors cache attached to each chart + * when a global plugin is registered or unregistered. In this case, the cache ID is + * incremented and descriptors are regenerated during following API calls. + * @private + */ + _cacheId: 0, + + /** + * Registers the given plugin(s) if not already registered. + * @param {Array|Object} plugins plugin instance(s). + */ + register: function(plugins) { + var p = this._plugins; + ([]).concat(plugins).forEach(function(plugin) { + if (p.indexOf(plugin) === -1) { + p.push(plugin); + } + }); + + this._cacheId++; + }, + + /** + * Unregisters the given plugin(s) only if registered. + * @param {Array|Object} plugins plugin instance(s). + */ + unregister: function(plugins) { + var p = this._plugins; + ([]).concat(plugins).forEach(function(plugin) { + var idx = p.indexOf(plugin); + if (idx !== -1) { + p.splice(idx, 1); + } + }); + + this._cacheId++; + }, + + /** + * Remove all registered plugins. + * @since 2.1.5 + */ + clear: function() { + this._plugins = []; + this._cacheId++; + }, + + /** + * Returns the number of registered plugins? + * @returns {Number} + * @since 2.1.5 + */ + count: function() { + return this._plugins.length; + }, + + /** + * Returns all registered plugin instances. + * @returns {Array} array of plugin objects. + * @since 2.1.5 + */ + getAll: function() { + return this._plugins; + }, + + /** + * Calls enabled plugins for `chart` on the specified hook and with the given args. + * This method immediately returns as soon as a plugin explicitly returns false. The + * returned value can be used, for instance, to interrupt the current action. + * @param {Object} chart - The chart instance for which plugins should be called. + * @param {String} hook - The name of the plugin method to call (e.g. 'beforeUpdate'). + * @param {Array} [args] - Extra arguments to apply to the hook call. + * @returns {Boolean} false if any of the plugins return false, else returns true. + */ + notify: function(chart, hook, args) { + var descriptors = this.descriptors(chart); + var ilen = descriptors.length; + var i, descriptor, plugin, params, method; + + for (i = 0; i < ilen; ++i) { + descriptor = descriptors[i]; + plugin = descriptor.plugin; + method = plugin[hook]; + if (typeof method === 'function') { + params = [chart].concat(args || []); + params.push(descriptor.options); + if (method.apply(plugin, params) === false) { + return false; + } + } + } + + return true; + }, + + /** + * Returns descriptors of enabled plugins for the given chart. + * @returns {Array} [{ plugin, options }] + * @private + */ + descriptors: function(chart) { + var cache = chart._plugins || (chart._plugins = {}); + if (cache.id === this._cacheId) { + return cache.descriptors; + } + + var plugins = []; + var descriptors = []; + var config = (chart && chart.config) || {}; + var options = (config.options && config.options.plugins) || {}; + + this._plugins.concat(config.plugins || []).forEach(function(plugin) { + var idx = plugins.indexOf(plugin); + if (idx !== -1) { + return; + } + + var id = plugin.id; + var opts = options[id]; + if (opts === false) { + return; + } + + if (opts === true) { + opts = helpers.clone(defaults.global.plugins[id]); + } + + plugins.push(plugin); + descriptors.push({ + plugin: plugin, + options: opts || {} + }); + }); + + cache.descriptors = descriptors; + cache.id = this._cacheId; + return descriptors; + } + }; + + /** + * Plugin extension hooks. + * @interface IPlugin + * @since 2.1.0 + */ + /** + * @method IPlugin#beforeInit + * @desc Called before initializing `chart`. + * @param {Chart.Controller} chart - The chart instance. + * @param {Object} options - The plugin options. + */ + /** + * @method IPlugin#afterInit + * @desc Called after `chart` has been initialized and before the first update. + * @param {Chart.Controller} chart - The chart instance. + * @param {Object} options - The plugin options. + */ + /** + * @method IPlugin#beforeUpdate + * @desc Called before updating `chart`. If any plugin returns `false`, the update + * is cancelled (and thus subsequent render(s)) until another `update` is triggered. + * @param {Chart.Controller} chart - The chart instance. + * @param {Object} options - The plugin options. + * @returns {Boolean} `false` to cancel the chart update. + */ + /** + * @method IPlugin#afterUpdate + * @desc Called after `chart` has been updated and before rendering. Note that this + * hook will not be called if the chart update has been previously cancelled. + * @param {Chart.Controller} chart - The chart instance. + * @param {Object} options - The plugin options. + */ + /** + * @method IPlugin#beforeDatasetsUpdate + * @desc Called before updating the `chart` datasets. If any plugin returns `false`, + * the datasets update is cancelled until another `update` is triggered. + * @param {Chart.Controller} chart - The chart instance. + * @param {Object} options - The plugin options. + * @returns {Boolean} false to cancel the datasets update. + * @since version 2.1.5 + */ + /** + * @method IPlugin#afterDatasetsUpdate + * @desc Called after the `chart` datasets have been updated. Note that this hook + * will not be called if the datasets update has been previously cancelled. + * @param {Chart.Controller} chart - The chart instance. + * @param {Object} options - The plugin options. + * @since version 2.1.5 + */ + /** + * @method IPlugin#beforeDatasetUpdate + * @desc Called before updating the `chart` dataset at the given `args.index`. If any plugin + * returns `false`, the datasets update is cancelled until another `update` is triggered. + * @param {Chart} chart - The chart instance. + * @param {Object} args - The call arguments. + * @param {Number} args.index - The dataset index. + * @param {Object} args.meta - The dataset metadata. + * @param {Object} options - The plugin options. + * @returns {Boolean} `false` to cancel the chart datasets drawing. + */ + /** + * @method IPlugin#afterDatasetUpdate + * @desc Called after the `chart` datasets at the given `args.index` has been updated. Note + * that this hook will not be called if the datasets update has been previously cancelled. + * @param {Chart} chart - The chart instance. + * @param {Object} args - The call arguments. + * @param {Number} args.index - The dataset index. + * @param {Object} args.meta - The dataset metadata. + * @param {Object} options - The plugin options. + */ + /** + * @method IPlugin#beforeLayout + * @desc Called before laying out `chart`. If any plugin returns `false`, + * the layout update is cancelled until another `update` is triggered. + * @param {Chart.Controller} chart - The chart instance. + * @param {Object} options - The plugin options. + * @returns {Boolean} `false` to cancel the chart layout. + */ + /** + * @method IPlugin#afterLayout + * @desc Called after the `chart` has been layed out. Note that this hook will not + * be called if the layout update has been previously cancelled. + * @param {Chart.Controller} chart - The chart instance. + * @param {Object} options - The plugin options. + */ + /** + * @method IPlugin#beforeRender + * @desc Called before rendering `chart`. If any plugin returns `false`, + * the rendering is cancelled until another `render` is triggered. + * @param {Chart.Controller} chart - The chart instance. + * @param {Object} options - The plugin options. + * @returns {Boolean} `false` to cancel the chart rendering. + */ + /** + * @method IPlugin#afterRender + * @desc Called after the `chart` has been fully rendered (and animation completed). Note + * that this hook will not be called if the rendering has been previously cancelled. + * @param {Chart.Controller} chart - The chart instance. + * @param {Object} options - The plugin options. + */ + /** + * @method IPlugin#beforeDraw + * @desc Called before drawing `chart` at every animation frame specified by the given + * easing value. If any plugin returns `false`, the frame drawing is cancelled until + * another `render` is triggered. + * @param {Chart.Controller} chart - The chart instance. + * @param {Number} easingValue - The current animation value, between 0.0 and 1.0. + * @param {Object} options - The plugin options. + * @returns {Boolean} `false` to cancel the chart drawing. + */ + /** + * @method IPlugin#afterDraw + * @desc Called after the `chart` has been drawn for the specific easing value. Note + * that this hook will not be called if the drawing has been previously cancelled. + * @param {Chart.Controller} chart - The chart instance. + * @param {Number} easingValue - The current animation value, between 0.0 and 1.0. + * @param {Object} options - The plugin options. + */ + /** + * @method IPlugin#beforeDatasetsDraw + * @desc Called before drawing the `chart` datasets. If any plugin returns `false`, + * the datasets drawing is cancelled until another `render` is triggered. + * @param {Chart.Controller} chart - The chart instance. + * @param {Number} easingValue - The current animation value, between 0.0 and 1.0. + * @param {Object} options - The plugin options. + * @returns {Boolean} `false` to cancel the chart datasets drawing. + */ + /** + * @method IPlugin#afterDatasetsDraw + * @desc Called after the `chart` datasets have been drawn. Note that this hook + * will not be called if the datasets drawing has been previously cancelled. + * @param {Chart.Controller} chart - The chart instance. + * @param {Number} easingValue - The current animation value, between 0.0 and 1.0. + * @param {Object} options - The plugin options. + */ + /** + * @method IPlugin#beforeDatasetDraw + * @desc Called before drawing the `chart` dataset at the given `args.index` (datasets + * are drawn in the reverse order). If any plugin returns `false`, the datasets drawing + * is cancelled until another `render` is triggered. + * @param {Chart} chart - The chart instance. + * @param {Object} args - The call arguments. + * @param {Number} args.index - The dataset index. + * @param {Object} args.meta - The dataset metadata. + * @param {Number} args.easingValue - The current animation value, between 0.0 and 1.0. + * @param {Object} options - The plugin options. + * @returns {Boolean} `false` to cancel the chart datasets drawing. + */ + /** + * @method IPlugin#afterDatasetDraw + * @desc Called after the `chart` datasets at the given `args.index` have been drawn + * (datasets are drawn in the reverse order). Note that this hook will not be called + * if the datasets drawing has been previously cancelled. + * @param {Chart} chart - The chart instance. + * @param {Object} args - The call arguments. + * @param {Number} args.index - The dataset index. + * @param {Object} args.meta - The dataset metadata. + * @param {Number} args.easingValue - The current animation value, between 0.0 and 1.0. + * @param {Object} options - The plugin options. + */ + /** + * @method IPlugin#beforeTooltipDraw + * @desc Called before drawing the `tooltip`. If any plugin returns `false`, + * the tooltip drawing is cancelled until another `render` is triggered. + * @param {Chart} chart - The chart instance. + * @param {Object} args - The call arguments. + * @param {Object} args.tooltip - The tooltip. + * @param {Number} args.easingValue - The current animation value, between 0.0 and 1.0. + * @param {Object} options - The plugin options. + * @returns {Boolean} `false` to cancel the chart tooltip drawing. + */ + /** + * @method IPlugin#afterTooltipDraw + * @desc Called after drawing the `tooltip`. Note that this hook will not + * be called if the tooltip drawing has been previously cancelled. + * @param {Chart} chart - The chart instance. + * @param {Object} args - The call arguments. + * @param {Object} args.tooltip - The tooltip. + * @param {Number} args.easingValue - The current animation value, between 0.0 and 1.0. + * @param {Object} options - The plugin options. + */ + /** + * @method IPlugin#beforeEvent + * @desc Called before processing the specified `event`. If any plugin returns `false`, + * the event will be discarded. + * @param {Chart.Controller} chart - The chart instance. + * @param {IEvent} event - The event object. + * @param {Object} options - The plugin options. + */ + /** + * @method IPlugin#afterEvent + * @desc Called after the `event` has been consumed. Note that this hook + * will not be called if the `event` has been previously discarded. + * @param {Chart.Controller} chart - The chart instance. + * @param {IEvent} event - The event object. + * @param {Object} options - The plugin options. + */ + /** + * @method IPlugin#resize + * @desc Called after the chart as been resized. + * @param {Chart.Controller} chart - The chart instance. + * @param {Number} size - The new canvas display size (eq. canvas.style width & height). + * @param {Object} options - The plugin options. + */ + /** + * @method IPlugin#destroy + * @desc Called after the chart as been destroyed. + * @param {Chart.Controller} chart - The chart instance. + * @param {Object} options - The plugin options. + */ + + /** + * Provided for backward compatibility, use Chart.plugins instead + * @namespace Chart.pluginService + * @deprecated since version 2.1.5 + * @todo remove at version 3 + * @private + */ + Chart.pluginService = Chart.plugins; + + /** + * Provided for backward compatibility, inheriting from Chart.PlugingBase has no + * effect, instead simply create/register plugins via plain JavaScript objects. + * @interface Chart.PluginBase + * @deprecated since version 2.5.0 + * @todo remove at version 3 + * @private + */ + Chart.PluginBase = Element.extend({}); +}; + +},{"25":25,"26":26,"45":45}],32:[function(require,module,exports){ +'use strict'; + +var defaults = require(25); +var Element = require(26); +var helpers = require(45); +var Ticks = require(34); + +defaults._set('scale', { + display: true, + position: 'left', + offset: false, + + // grid line settings + gridLines: { + display: true, + color: 'rgba(0, 0, 0, 0.1)', + lineWidth: 1, + drawBorder: true, + drawOnChartArea: true, + drawTicks: true, + tickMarkLength: 10, + zeroLineWidth: 1, + zeroLineColor: 'rgba(0,0,0,0.25)', + zeroLineBorderDash: [], + zeroLineBorderDashOffset: 0.0, + offsetGridLines: false, + borderDash: [], + borderDashOffset: 0.0 + }, + + // scale label + scaleLabel: { + // display property + display: false, + + // actual label + labelString: '', + + // line height + lineHeight: 1.2, + + // top/bottom padding + padding: { + top: 4, + bottom: 4 + } + }, + + // label settings + ticks: { + beginAtZero: false, + minRotation: 0, + maxRotation: 50, + mirror: false, + padding: 0, + reverse: false, + display: true, + autoSkip: true, + autoSkipPadding: 0, + labelOffset: 0, + // We pass through arrays to be rendered as multiline labels, we convert Others to strings here. + callback: Ticks.formatters.values, + minor: {}, + major: {} + } +}); + +function labelsFromTicks(ticks) { + var labels = []; + var i, ilen; + + for (i = 0, ilen = ticks.length; i < ilen; ++i) { + labels.push(ticks[i].label); + } + + return labels; +} + +function getLineValue(scale, index, offsetGridLines) { + var lineValue = scale.getPixelForTick(index); + + if (offsetGridLines) { + if (index === 0) { + lineValue -= (scale.getPixelForTick(1) - lineValue) / 2; + } else { + lineValue -= (lineValue - scale.getPixelForTick(index - 1)) / 2; + } + } + return lineValue; +} + +module.exports = function(Chart) { + + function computeTextSize(context, tick, font) { + return helpers.isArray(tick) ? + helpers.longestText(context, font, tick) : + context.measureText(tick).width; + } + + function parseFontOptions(options) { + var valueOrDefault = helpers.valueOrDefault; + var globalDefaults = defaults.global; + var size = valueOrDefault(options.fontSize, globalDefaults.defaultFontSize); + var style = valueOrDefault(options.fontStyle, globalDefaults.defaultFontStyle); + var family = valueOrDefault(options.fontFamily, globalDefaults.defaultFontFamily); + + return { + size: size, + style: style, + family: family, + font: helpers.fontString(size, style, family) + }; + } + + function parseLineHeight(options) { + return helpers.options.toLineHeight( + helpers.valueOrDefault(options.lineHeight, 1.2), + helpers.valueOrDefault(options.fontSize, defaults.global.defaultFontSize)); + } + + Chart.Scale = Element.extend({ + /** + * Get the padding needed for the scale + * @method getPadding + * @private + * @returns {Padding} the necessary padding + */ + getPadding: function() { + var me = this; + return { + left: me.paddingLeft || 0, + top: me.paddingTop || 0, + right: me.paddingRight || 0, + bottom: me.paddingBottom || 0 + }; + }, + + /** + * Returns the scale tick objects ({label, major}) + * @since 2.7 + */ + getTicks: function() { + return this._ticks; + }, + + // These methods are ordered by lifecyle. Utilities then follow. + // Any function defined here is inherited by all scale types. + // Any function can be extended by the scale type + + mergeTicksOptions: function() { + var ticks = this.options.ticks; + if (ticks.minor === false) { + ticks.minor = { + display: false + }; + } + if (ticks.major === false) { + ticks.major = { + display: false + }; + } + for (var key in ticks) { + if (key !== 'major' && key !== 'minor') { + if (typeof ticks.minor[key] === 'undefined') { + ticks.minor[key] = ticks[key]; + } + if (typeof ticks.major[key] === 'undefined') { + ticks.major[key] = ticks[key]; + } + } + } + }, + beforeUpdate: function() { + helpers.callback(this.options.beforeUpdate, [this]); + }, + update: function(maxWidth, maxHeight, margins) { + var me = this; + var i, ilen, labels, label, ticks, tick; + + // Update Lifecycle - Probably don't want to ever extend or overwrite this function ;) + me.beforeUpdate(); + + // Absorb the master measurements + me.maxWidth = maxWidth; + me.maxHeight = maxHeight; + me.margins = helpers.extend({ + left: 0, + right: 0, + top: 0, + bottom: 0 + }, margins); + me.longestTextCache = me.longestTextCache || {}; + + // Dimensions + me.beforeSetDimensions(); + me.setDimensions(); + me.afterSetDimensions(); + + // Data min/max + me.beforeDataLimits(); + me.determineDataLimits(); + me.afterDataLimits(); + + // Ticks - `this.ticks` is now DEPRECATED! + // Internal ticks are now stored as objects in the PRIVATE `this._ticks` member + // and must not be accessed directly from outside this class. `this.ticks` being + // around for long time and not marked as private, we can't change its structure + // without unexpected breaking changes. If you need to access the scale ticks, + // use scale.getTicks() instead. + + me.beforeBuildTicks(); + + // New implementations should return an array of objects but for BACKWARD COMPAT, + // we still support no return (`this.ticks` internally set by calling this method). + ticks = me.buildTicks() || []; + + me.afterBuildTicks(); + + me.beforeTickToLabelConversion(); + + // New implementations should return the formatted tick labels but for BACKWARD + // COMPAT, we still support no return (`this.ticks` internally changed by calling + // this method and supposed to contain only string values). + labels = me.convertTicksToLabels(ticks) || me.ticks; + + me.afterTickToLabelConversion(); + + me.ticks = labels; // BACKWARD COMPATIBILITY + + // IMPORTANT: from this point, we consider that `this.ticks` will NEVER change! + + // BACKWARD COMPAT: synchronize `_ticks` with labels (so potentially `this.ticks`) + for (i = 0, ilen = labels.length; i < ilen; ++i) { + label = labels[i]; + tick = ticks[i]; + if (!tick) { + ticks.push(tick = { + label: label, + major: false + }); + } else { + tick.label = label; + } + } + + me._ticks = ticks; + + // Tick Rotation + me.beforeCalculateTickRotation(); + me.calculateTickRotation(); + me.afterCalculateTickRotation(); + // Fit + me.beforeFit(); + me.fit(); + me.afterFit(); + // + me.afterUpdate(); + + return me.minSize; + + }, + afterUpdate: function() { + helpers.callback(this.options.afterUpdate, [this]); + }, + + // + + beforeSetDimensions: function() { + helpers.callback(this.options.beforeSetDimensions, [this]); + }, + setDimensions: function() { + var me = this; + // Set the unconstrained dimension before label rotation + if (me.isHorizontal()) { + // Reset position before calculating rotation + me.width = me.maxWidth; + me.left = 0; + me.right = me.width; + } else { + me.height = me.maxHeight; + + // Reset position before calculating rotation + me.top = 0; + me.bottom = me.height; + } + + // Reset padding + me.paddingLeft = 0; + me.paddingTop = 0; + me.paddingRight = 0; + me.paddingBottom = 0; + }, + afterSetDimensions: function() { + helpers.callback(this.options.afterSetDimensions, [this]); + }, + + // Data limits + beforeDataLimits: function() { + helpers.callback(this.options.beforeDataLimits, [this]); + }, + determineDataLimits: helpers.noop, + afterDataLimits: function() { + helpers.callback(this.options.afterDataLimits, [this]); + }, + + // + beforeBuildTicks: function() { + helpers.callback(this.options.beforeBuildTicks, [this]); + }, + buildTicks: helpers.noop, + afterBuildTicks: function() { + helpers.callback(this.options.afterBuildTicks, [this]); + }, + + beforeTickToLabelConversion: function() { + helpers.callback(this.options.beforeTickToLabelConversion, [this]); + }, + convertTicksToLabels: function() { + var me = this; + // Convert ticks to strings + var tickOpts = me.options.ticks; + me.ticks = me.ticks.map(tickOpts.userCallback || tickOpts.callback, this); + }, + afterTickToLabelConversion: function() { + helpers.callback(this.options.afterTickToLabelConversion, [this]); + }, + + // + + beforeCalculateTickRotation: function() { + helpers.callback(this.options.beforeCalculateTickRotation, [this]); + }, + calculateTickRotation: function() { + var me = this; + var context = me.ctx; + var tickOpts = me.options.ticks; + var labels = labelsFromTicks(me._ticks); + + // Get the width of each grid by calculating the difference + // between x offsets between 0 and 1. + var tickFont = parseFontOptions(tickOpts); + context.font = tickFont.font; + + var labelRotation = tickOpts.minRotation || 0; + + if (labels.length && me.options.display && me.isHorizontal()) { + var originalLabelWidth = helpers.longestText(context, tickFont.font, labels, me.longestTextCache); + var labelWidth = originalLabelWidth; + var cosRotation, sinRotation; + + // Allow 3 pixels x2 padding either side for label readability + var tickWidth = me.getPixelForTick(1) - me.getPixelForTick(0) - 6; + + // Max label rotation can be set or default to 90 - also act as a loop counter + while (labelWidth > tickWidth && labelRotation < tickOpts.maxRotation) { + var angleRadians = helpers.toRadians(labelRotation); + cosRotation = Math.cos(angleRadians); + sinRotation = Math.sin(angleRadians); + + if (sinRotation * originalLabelWidth > me.maxHeight) { + // go back one step + labelRotation--; + break; + } + + labelRotation++; + labelWidth = cosRotation * originalLabelWidth; + } + } + + me.labelRotation = labelRotation; + }, + afterCalculateTickRotation: function() { + helpers.callback(this.options.afterCalculateTickRotation, [this]); + }, + + // + + beforeFit: function() { + helpers.callback(this.options.beforeFit, [this]); + }, + fit: function() { + var me = this; + // Reset + var minSize = me.minSize = { + width: 0, + height: 0 + }; + + var labels = labelsFromTicks(me._ticks); + + var opts = me.options; + var tickOpts = opts.ticks; + var scaleLabelOpts = opts.scaleLabel; + var gridLineOpts = opts.gridLines; + var display = opts.display; + var isHorizontal = me.isHorizontal(); + + var tickFont = parseFontOptions(tickOpts); + var tickMarkLength = opts.gridLines.tickMarkLength; + + // Width + if (isHorizontal) { + // subtract the margins to line up with the chartArea if we are a full width scale + minSize.width = me.isFullWidth() ? me.maxWidth - me.margins.left - me.margins.right : me.maxWidth; + } else { + minSize.width = display && gridLineOpts.drawTicks ? tickMarkLength : 0; + } + + // height + if (isHorizontal) { + minSize.height = display && gridLineOpts.drawTicks ? tickMarkLength : 0; + } else { + minSize.height = me.maxHeight; // fill all the height + } + + // Are we showing a title for the scale? + if (scaleLabelOpts.display && display) { + var scaleLabelLineHeight = parseLineHeight(scaleLabelOpts); + var scaleLabelPadding = helpers.options.toPadding(scaleLabelOpts.padding); + var deltaHeight = scaleLabelLineHeight + scaleLabelPadding.height; + + if (isHorizontal) { + minSize.height += deltaHeight; + } else { + minSize.width += deltaHeight; + } + } + + // Don't bother fitting the ticks if we are not showing them + if (tickOpts.display && display) { + var largestTextWidth = helpers.longestText(me.ctx, tickFont.font, labels, me.longestTextCache); + var tallestLabelHeightInLines = helpers.numberOfLabelLines(labels); + var lineSpace = tickFont.size * 0.5; + var tickPadding = me.options.ticks.padding; + + if (isHorizontal) { + // A horizontal axis is more constrained by the height. + me.longestLabelWidth = largestTextWidth; + + var angleRadians = helpers.toRadians(me.labelRotation); + var cosRotation = Math.cos(angleRadians); + var sinRotation = Math.sin(angleRadians); + + // TODO - improve this calculation + var labelHeight = (sinRotation * largestTextWidth) + + (tickFont.size * tallestLabelHeightInLines) + + (lineSpace * (tallestLabelHeightInLines - 1)) + + lineSpace; // padding + + minSize.height = Math.min(me.maxHeight, minSize.height + labelHeight + tickPadding); + + me.ctx.font = tickFont.font; + var firstLabelWidth = computeTextSize(me.ctx, labels[0], tickFont.font); + var lastLabelWidth = computeTextSize(me.ctx, labels[labels.length - 1], tickFont.font); + + // Ensure that our ticks are always inside the canvas. When rotated, ticks are right aligned + // which means that the right padding is dominated by the font height + if (me.labelRotation !== 0) { + me.paddingLeft = opts.position === 'bottom' ? (cosRotation * firstLabelWidth) + 3 : (cosRotation * lineSpace) + 3; // add 3 px to move away from canvas edges + me.paddingRight = opts.position === 'bottom' ? (cosRotation * lineSpace) + 3 : (cosRotation * lastLabelWidth) + 3; + } else { + me.paddingLeft = firstLabelWidth / 2 + 3; // add 3 px to move away from canvas edges + me.paddingRight = lastLabelWidth / 2 + 3; + } + } else { + // A vertical axis is more constrained by the width. Labels are the + // dominant factor here, so get that length first and account for padding + if (tickOpts.mirror) { + largestTextWidth = 0; + } else { + // use lineSpace for consistency with horizontal axis + // tickPadding is not implemented for horizontal + largestTextWidth += tickPadding + lineSpace; + } + + minSize.width = Math.min(me.maxWidth, minSize.width + largestTextWidth); + + me.paddingTop = tickFont.size / 2; + me.paddingBottom = tickFont.size / 2; + } + } + + me.handleMargins(); + + me.width = minSize.width; + me.height = minSize.height; + }, + + /** + * Handle margins and padding interactions + * @private + */ + handleMargins: function() { + var me = this; + if (me.margins) { + me.paddingLeft = Math.max(me.paddingLeft - me.margins.left, 0); + me.paddingTop = Math.max(me.paddingTop - me.margins.top, 0); + me.paddingRight = Math.max(me.paddingRight - me.margins.right, 0); + me.paddingBottom = Math.max(me.paddingBottom - me.margins.bottom, 0); + } + }, + + afterFit: function() { + helpers.callback(this.options.afterFit, [this]); + }, + + // Shared Methods + isHorizontal: function() { + return this.options.position === 'top' || this.options.position === 'bottom'; + }, + isFullWidth: function() { + return (this.options.fullWidth); + }, + + // Get the correct value. NaN bad inputs, If the value type is object get the x or y based on whether we are horizontal or not + getRightValue: function(rawValue) { + // Null and undefined values first + if (helpers.isNullOrUndef(rawValue)) { + return NaN; + } + // isNaN(object) returns true, so make sure NaN is checking for a number; Discard Infinite values + if (typeof rawValue === 'number' && !isFinite(rawValue)) { + return NaN; + } + // If it is in fact an object, dive in one more level + if (rawValue) { + if (this.isHorizontal()) { + if (rawValue.x !== undefined) { + return this.getRightValue(rawValue.x); + } + } else if (rawValue.y !== undefined) { + return this.getRightValue(rawValue.y); + } + } + + // Value is good, return it + return rawValue; + }, + + /** + * Used to get the value to display in the tooltip for the data at the given index + * @param index + * @param datasetIndex + */ + getLabelForIndex: helpers.noop, + + /** + * Returns the location of the given data point. Value can either be an index or a numerical value + * The coordinate (0, 0) is at the upper-left corner of the canvas + * @param value + * @param index + * @param datasetIndex + */ + getPixelForValue: helpers.noop, + + /** + * Used to get the data value from a given pixel. This is the inverse of getPixelForValue + * The coordinate (0, 0) is at the upper-left corner of the canvas + * @param pixel + */ + getValueForPixel: helpers.noop, + + /** + * Returns the location of the tick at the given index + * The coordinate (0, 0) is at the upper-left corner of the canvas + */ + getPixelForTick: function(index) { + var me = this; + var offset = me.options.offset; + if (me.isHorizontal()) { + var innerWidth = me.width - (me.paddingLeft + me.paddingRight); + var tickWidth = innerWidth / Math.max((me._ticks.length - (offset ? 0 : 1)), 1); + var pixel = (tickWidth * index) + me.paddingLeft; + + if (offset) { + pixel += tickWidth / 2; + } + + var finalVal = me.left + Math.round(pixel); + finalVal += me.isFullWidth() ? me.margins.left : 0; + return finalVal; + } + var innerHeight = me.height - (me.paddingTop + me.paddingBottom); + return me.top + (index * (innerHeight / (me._ticks.length - 1))); + }, + + /** + * Utility for getting the pixel location of a percentage of scale + * The coordinate (0, 0) is at the upper-left corner of the canvas + */ + getPixelForDecimal: function(decimal) { + var me = this; + if (me.isHorizontal()) { + var innerWidth = me.width - (me.paddingLeft + me.paddingRight); + var valueOffset = (innerWidth * decimal) + me.paddingLeft; + + var finalVal = me.left + Math.round(valueOffset); + finalVal += me.isFullWidth() ? me.margins.left : 0; + return finalVal; + } + return me.top + (decimal * me.height); + }, + + /** + * Returns the pixel for the minimum chart value + * The coordinate (0, 0) is at the upper-left corner of the canvas + */ + getBasePixel: function() { + return this.getPixelForValue(this.getBaseValue()); + }, + + getBaseValue: function() { + var me = this; + var min = me.min; + var max = me.max; + + return me.beginAtZero ? 0 : + min < 0 && max < 0 ? max : + min > 0 && max > 0 ? min : + 0; + }, + + /** + * Returns a subset of ticks to be plotted to avoid overlapping labels. + * @private + */ + _autoSkip: function(ticks) { + var skipRatio; + var me = this; + var isHorizontal = me.isHorizontal(); + var optionTicks = me.options.ticks.minor; + var tickCount = ticks.length; + var labelRotationRadians = helpers.toRadians(me.labelRotation); + var cosRotation = Math.cos(labelRotationRadians); + var longestRotatedLabel = me.longestLabelWidth * cosRotation; + var result = []; + var i, tick, shouldSkip; + + // figure out the maximum number of gridlines to show + var maxTicks; + if (optionTicks.maxTicksLimit) { + maxTicks = optionTicks.maxTicksLimit; + } + + if (isHorizontal) { + skipRatio = false; + + if ((longestRotatedLabel + optionTicks.autoSkipPadding) * tickCount > (me.width - (me.paddingLeft + me.paddingRight))) { + skipRatio = 1 + Math.floor(((longestRotatedLabel + optionTicks.autoSkipPadding) * tickCount) / (me.width - (me.paddingLeft + me.paddingRight))); + } + + // if they defined a max number of optionTicks, + // increase skipRatio until that number is met + if (maxTicks && tickCount > maxTicks) { + skipRatio = Math.max(skipRatio, Math.floor(tickCount / maxTicks)); + } + } + + for (i = 0; i < tickCount; i++) { + tick = ticks[i]; + + // Since we always show the last tick,we need may need to hide the last shown one before + shouldSkip = (skipRatio > 1 && i % skipRatio > 0) || (i % skipRatio === 0 && i + skipRatio >= tickCount); + if (shouldSkip && i !== tickCount - 1) { + // leave tick in place but make sure it's not displayed (#4635) + delete tick.label; + } + result.push(tick); + } + return result; + }, + + // Actually draw the scale on the canvas + // @param {rectangle} chartArea : the area of the chart to draw full grid lines on + draw: function(chartArea) { + var me = this; + var options = me.options; + if (!options.display) { + return; + } + + var context = me.ctx; + var globalDefaults = defaults.global; + var optionTicks = options.ticks.minor; + var optionMajorTicks = options.ticks.major || optionTicks; + var gridLines = options.gridLines; + var scaleLabel = options.scaleLabel; + + var isRotated = me.labelRotation !== 0; + var isHorizontal = me.isHorizontal(); + + var ticks = optionTicks.autoSkip ? me._autoSkip(me.getTicks()) : me.getTicks(); + var tickFontColor = helpers.valueOrDefault(optionTicks.fontColor, globalDefaults.defaultFontColor); + var tickFont = parseFontOptions(optionTicks); + var majorTickFontColor = helpers.valueOrDefault(optionMajorTicks.fontColor, globalDefaults.defaultFontColor); + var majorTickFont = parseFontOptions(optionMajorTicks); + + var tl = gridLines.drawTicks ? gridLines.tickMarkLength : 0; + + var scaleLabelFontColor = helpers.valueOrDefault(scaleLabel.fontColor, globalDefaults.defaultFontColor); + var scaleLabelFont = parseFontOptions(scaleLabel); + var scaleLabelPadding = helpers.options.toPadding(scaleLabel.padding); + var labelRotationRadians = helpers.toRadians(me.labelRotation); + + var itemsToDraw = []; + + var xTickStart = options.position === 'right' ? me.left : me.right - tl; + var xTickEnd = options.position === 'right' ? me.left + tl : me.right; + var yTickStart = options.position === 'bottom' ? me.top : me.bottom - tl; + var yTickEnd = options.position === 'bottom' ? me.top + tl : me.bottom; + + helpers.each(ticks, function(tick, index) { + // autoskipper skipped this tick (#4635) + if (helpers.isNullOrUndef(tick.label)) { + return; + } + + var label = tick.label; + var lineWidth, lineColor, borderDash, borderDashOffset; + if (index === me.zeroLineIndex && options.offset === gridLines.offsetGridLines) { + // Draw the first index specially + lineWidth = gridLines.zeroLineWidth; + lineColor = gridLines.zeroLineColor; + borderDash = gridLines.zeroLineBorderDash; + borderDashOffset = gridLines.zeroLineBorderDashOffset; + } else { + lineWidth = helpers.valueAtIndexOrDefault(gridLines.lineWidth, index); + lineColor = helpers.valueAtIndexOrDefault(gridLines.color, index); + borderDash = helpers.valueOrDefault(gridLines.borderDash, globalDefaults.borderDash); + borderDashOffset = helpers.valueOrDefault(gridLines.borderDashOffset, globalDefaults.borderDashOffset); + } + + // Common properties + var tx1, ty1, tx2, ty2, x1, y1, x2, y2, labelX, labelY; + var textAlign = 'middle'; + var textBaseline = 'middle'; + var tickPadding = optionTicks.padding; + + if (isHorizontal) { + var labelYOffset = tl + tickPadding; + + if (options.position === 'bottom') { + // bottom + textBaseline = !isRotated ? 'top' : 'middle'; + textAlign = !isRotated ? 'center' : 'right'; + labelY = me.top + labelYOffset; + } else { + // top + textBaseline = !isRotated ? 'bottom' : 'middle'; + textAlign = !isRotated ? 'center' : 'left'; + labelY = me.bottom - labelYOffset; + } + + var xLineValue = getLineValue(me, index, gridLines.offsetGridLines && ticks.length > 1); + if (xLineValue < me.left) { + lineColor = 'rgba(0,0,0,0)'; + } + xLineValue += helpers.aliasPixel(lineWidth); + + labelX = me.getPixelForTick(index) + optionTicks.labelOffset; // x values for optionTicks (need to consider offsetLabel option) + + tx1 = tx2 = x1 = x2 = xLineValue; + ty1 = yTickStart; + ty2 = yTickEnd; + y1 = chartArea.top; + y2 = chartArea.bottom; + } else { + var isLeft = options.position === 'left'; + var labelXOffset; + + if (optionTicks.mirror) { + textAlign = isLeft ? 'left' : 'right'; + labelXOffset = tickPadding; + } else { + textAlign = isLeft ? 'right' : 'left'; + labelXOffset = tl + tickPadding; + } + + labelX = isLeft ? me.right - labelXOffset : me.left + labelXOffset; + + var yLineValue = getLineValue(me, index, gridLines.offsetGridLines && ticks.length > 1); + if (yLineValue < me.top) { + lineColor = 'rgba(0,0,0,0)'; + } + yLineValue += helpers.aliasPixel(lineWidth); + + labelY = me.getPixelForTick(index) + optionTicks.labelOffset; + + tx1 = xTickStart; + tx2 = xTickEnd; + x1 = chartArea.left; + x2 = chartArea.right; + ty1 = ty2 = y1 = y2 = yLineValue; + } + + itemsToDraw.push({ + tx1: tx1, + ty1: ty1, + tx2: tx2, + ty2: ty2, + x1: x1, + y1: y1, + x2: x2, + y2: y2, + labelX: labelX, + labelY: labelY, + glWidth: lineWidth, + glColor: lineColor, + glBorderDash: borderDash, + glBorderDashOffset: borderDashOffset, + rotation: -1 * labelRotationRadians, + label: label, + major: tick.major, + textBaseline: textBaseline, + textAlign: textAlign + }); + }); + + // Draw all of the tick labels, tick marks, and grid lines at the correct places + helpers.each(itemsToDraw, function(itemToDraw) { + if (gridLines.display) { + context.save(); + context.lineWidth = itemToDraw.glWidth; + context.strokeStyle = itemToDraw.glColor; + if (context.setLineDash) { + context.setLineDash(itemToDraw.glBorderDash); + context.lineDashOffset = itemToDraw.glBorderDashOffset; + } + + context.beginPath(); + + if (gridLines.drawTicks) { + context.moveTo(itemToDraw.tx1, itemToDraw.ty1); + context.lineTo(itemToDraw.tx2, itemToDraw.ty2); + } + + if (gridLines.drawOnChartArea) { + context.moveTo(itemToDraw.x1, itemToDraw.y1); + context.lineTo(itemToDraw.x2, itemToDraw.y2); + } + + context.stroke(); + context.restore(); + } + + if (optionTicks.display) { + // Make sure we draw text in the correct color and font + context.save(); + context.translate(itemToDraw.labelX, itemToDraw.labelY); + context.rotate(itemToDraw.rotation); + context.font = itemToDraw.major ? majorTickFont.font : tickFont.font; + context.fillStyle = itemToDraw.major ? majorTickFontColor : tickFontColor; + context.textBaseline = itemToDraw.textBaseline; + context.textAlign = itemToDraw.textAlign; + + var label = itemToDraw.label; + if (helpers.isArray(label)) { + for (var i = 0, y = 0; i < label.length; ++i) { + // We just make sure the multiline element is a string here.. + context.fillText('' + label[i], 0, y); + // apply same lineSpacing as calculated @ L#320 + y += (tickFont.size * 1.5); + } + } else { + context.fillText(label, 0, 0); + } + context.restore(); + } + }); + + if (scaleLabel.display) { + // Draw the scale label + var scaleLabelX; + var scaleLabelY; + var rotation = 0; + var halfLineHeight = parseLineHeight(scaleLabel) / 2; + + if (isHorizontal) { + scaleLabelX = me.left + ((me.right - me.left) / 2); // midpoint of the width + scaleLabelY = options.position === 'bottom' + ? me.bottom - halfLineHeight - scaleLabelPadding.bottom + : me.top + halfLineHeight + scaleLabelPadding.top; + } else { + var isLeft = options.position === 'left'; + scaleLabelX = isLeft + ? me.left + halfLineHeight + scaleLabelPadding.top + : me.right - halfLineHeight - scaleLabelPadding.top; + scaleLabelY = me.top + ((me.bottom - me.top) / 2); + rotation = isLeft ? -0.5 * Math.PI : 0.5 * Math.PI; + } + + context.save(); + context.translate(scaleLabelX, scaleLabelY); + context.rotate(rotation); + context.textAlign = 'center'; + context.textBaseline = 'middle'; + context.fillStyle = scaleLabelFontColor; // render in correct colour + context.font = scaleLabelFont.font; + context.fillText(scaleLabel.labelString, 0, 0); + context.restore(); + } + + if (gridLines.drawBorder) { + // Draw the line at the edge of the axis + context.lineWidth = helpers.valueAtIndexOrDefault(gridLines.lineWidth, 0); + context.strokeStyle = helpers.valueAtIndexOrDefault(gridLines.color, 0); + var x1 = me.left; + var x2 = me.right; + var y1 = me.top; + var y2 = me.bottom; + + var aliasPixel = helpers.aliasPixel(context.lineWidth); + if (isHorizontal) { + y1 = y2 = options.position === 'top' ? me.bottom : me.top; + y1 += aliasPixel; + y2 += aliasPixel; + } else { + x1 = x2 = options.position === 'left' ? me.right : me.left; + x1 += aliasPixel; + x2 += aliasPixel; + } + + context.beginPath(); + context.moveTo(x1, y1); + context.lineTo(x2, y2); + context.stroke(); + } + } + }); +}; + +},{"25":25,"26":26,"34":34,"45":45}],33:[function(require,module,exports){ +'use strict'; + +var defaults = require(25); +var helpers = require(45); + +module.exports = function(Chart) { + + Chart.scaleService = { + // Scale registration object. Extensions can register new scale types (such as log or DB scales) and then + // use the new chart options to grab the correct scale + constructors: {}, + // Use a registration function so that we can move to an ES6 map when we no longer need to support + // old browsers + + // Scale config defaults + defaults: {}, + registerScaleType: function(type, scaleConstructor, scaleDefaults) { + this.constructors[type] = scaleConstructor; + this.defaults[type] = helpers.clone(scaleDefaults); + }, + getScaleConstructor: function(type) { + return this.constructors.hasOwnProperty(type) ? this.constructors[type] : undefined; + }, + getScaleDefaults: function(type) { + // Return the scale defaults merged with the global settings so that we always use the latest ones + return this.defaults.hasOwnProperty(type) ? helpers.merge({}, [defaults.scale, this.defaults[type]]) : {}; + }, + updateScaleDefaults: function(type, additions) { + var me = this; + if (me.defaults.hasOwnProperty(type)) { + me.defaults[type] = helpers.extend(me.defaults[type], additions); + } + }, + addScalesToLayout: function(chart) { + // Adds each scale to the chart.boxes array to be sized accordingly + helpers.each(chart.scales, function(scale) { + // Set ILayoutItem parameters for backwards compatibility + scale.fullWidth = scale.options.fullWidth; + scale.position = scale.options.position; + scale.weight = scale.options.weight; + Chart.layoutService.addBox(chart, scale); + }); + } + }; +}; + +},{"25":25,"45":45}],34:[function(require,module,exports){ +'use strict'; + +var helpers = require(45); + +/** + * Namespace to hold static tick generation functions + * @namespace Chart.Ticks + */ +module.exports = { + /** + * Namespace to hold generators for different types of ticks + * @namespace Chart.Ticks.generators + */ + generators: { + /** + * Interface for the options provided to the numeric tick generator + * @interface INumericTickGenerationOptions + */ + /** + * The maximum number of ticks to display + * @name INumericTickGenerationOptions#maxTicks + * @type Number + */ + /** + * The distance between each tick. + * @name INumericTickGenerationOptions#stepSize + * @type Number + * @optional + */ + /** + * Forced minimum for the ticks. If not specified, the minimum of the data range is used to calculate the tick minimum + * @name INumericTickGenerationOptions#min + * @type Number + * @optional + */ + /** + * The maximum value of the ticks. If not specified, the maximum of the data range is used to calculate the tick maximum + * @name INumericTickGenerationOptions#max + * @type Number + * @optional + */ + + /** + * Generate a set of linear ticks + * @method Chart.Ticks.generators.linear + * @param generationOptions {INumericTickGenerationOptions} the options used to generate the ticks + * @param dataRange {IRange} the range of the data + * @returns {Array} array of tick values + */ + linear: function(generationOptions, dataRange) { + var ticks = []; + // To get a "nice" value for the tick spacing, we will use the appropriately named + // "nice number" algorithm. See http://stackoverflow.com/questions/8506881/nice-label-algorithm-for-charts-with-minimum-ticks + // for details. + + var spacing; + if (generationOptions.stepSize && generationOptions.stepSize > 0) { + spacing = generationOptions.stepSize; + } else { + var niceRange = helpers.niceNum(dataRange.max - dataRange.min, false); + spacing = helpers.niceNum(niceRange / (generationOptions.maxTicks - 1), true); + } + var niceMin = Math.floor(dataRange.min / spacing) * spacing; + var niceMax = Math.ceil(dataRange.max / spacing) * spacing; + + // If min, max and stepSize is set and they make an evenly spaced scale use it. + if (generationOptions.min && generationOptions.max && generationOptions.stepSize) { + // If very close to our whole number, use it. + if (helpers.almostWhole((generationOptions.max - generationOptions.min) / generationOptions.stepSize, spacing / 1000)) { + niceMin = generationOptions.min; + niceMax = generationOptions.max; + } + } + + var numSpaces = (niceMax - niceMin) / spacing; + // If very close to our rounded value, use it. + if (helpers.almostEquals(numSpaces, Math.round(numSpaces), spacing / 1000)) { + numSpaces = Math.round(numSpaces); + } else { + numSpaces = Math.ceil(numSpaces); + } + + // Put the values into the ticks array + ticks.push(generationOptions.min !== undefined ? generationOptions.min : niceMin); + for (var j = 1; j < numSpaces; ++j) { + ticks.push(niceMin + (j * spacing)); + } + ticks.push(generationOptions.max !== undefined ? generationOptions.max : niceMax); + + return ticks; + }, + + /** + * Generate a set of logarithmic ticks + * @method Chart.Ticks.generators.logarithmic + * @param generationOptions {INumericTickGenerationOptions} the options used to generate the ticks + * @param dataRange {IRange} the range of the data + * @returns {Array} array of tick values + */ + logarithmic: function(generationOptions, dataRange) { + var ticks = []; + var valueOrDefault = helpers.valueOrDefault; + + // Figure out what the max number of ticks we can support it is based on the size of + // the axis area. For now, we say that the minimum tick spacing in pixels must be 50 + // We also limit the maximum number of ticks to 11 which gives a nice 10 squares on + // the graph + var tickVal = valueOrDefault(generationOptions.min, Math.pow(10, Math.floor(helpers.log10(dataRange.min)))); + + var endExp = Math.floor(helpers.log10(dataRange.max)); + var endSignificand = Math.ceil(dataRange.max / Math.pow(10, endExp)); + var exp, significand; + + if (tickVal === 0) { + exp = Math.floor(helpers.log10(dataRange.minNotZero)); + significand = Math.floor(dataRange.minNotZero / Math.pow(10, exp)); + + ticks.push(tickVal); + tickVal = significand * Math.pow(10, exp); + } else { + exp = Math.floor(helpers.log10(tickVal)); + significand = Math.floor(tickVal / Math.pow(10, exp)); + } + + do { + ticks.push(tickVal); + + ++significand; + if (significand === 10) { + significand = 1; + ++exp; + } + + tickVal = significand * Math.pow(10, exp); + } while (exp < endExp || (exp === endExp && significand < endSignificand)); + + var lastTick = valueOrDefault(generationOptions.max, tickVal); + ticks.push(lastTick); + + return ticks; + } + }, + + /** + * Namespace to hold formatters for different types of ticks + * @namespace Chart.Ticks.formatters + */ + formatters: { + /** + * Formatter for value labels + * @method Chart.Ticks.formatters.values + * @param value the value to display + * @return {String|Array} the label to display + */ + values: function(value) { + return helpers.isArray(value) ? value : '' + value; + }, + + /** + * Formatter for linear numeric ticks + * @method Chart.Ticks.formatters.linear + * @param tickValue {Number} the value to be formatted + * @param index {Number} the position of the tickValue parameter in the ticks array + * @param ticks {Array} the list of ticks being converted + * @return {String} string representation of the tickValue parameter + */ + linear: function(tickValue, index, ticks) { + // If we have lots of ticks, don't use the ones + var delta = ticks.length > 3 ? ticks[2] - ticks[1] : ticks[1] - ticks[0]; + + // If we have a number like 2.5 as the delta, figure out how many decimal places we need + if (Math.abs(delta) > 1) { + if (tickValue !== Math.floor(tickValue)) { + // not an integer + delta = tickValue - Math.floor(tickValue); + } + } + + var logDelta = helpers.log10(Math.abs(delta)); + var tickString = ''; + + if (tickValue !== 0) { + var numDecimal = -1 * Math.floor(logDelta); + numDecimal = Math.max(Math.min(numDecimal, 20), 0); // toFixed has a max of 20 decimal places + tickString = tickValue.toFixed(numDecimal); + } else { + tickString = '0'; // never show decimal places for 0 + } + + return tickString; + }, + + logarithmic: function(tickValue, index, ticks) { + var remain = tickValue / (Math.pow(10, Math.floor(helpers.log10(tickValue)))); + + if (tickValue === 0) { + return '0'; + } else if (remain === 1 || remain === 2 || remain === 5 || index === 0 || index === ticks.length - 1) { + return tickValue.toExponential(); + } + return ''; + } + } +}; + +},{"45":45}],35:[function(require,module,exports){ +'use strict'; + +var defaults = require(25); +var Element = require(26); +var helpers = require(45); + +defaults._set('global', { + tooltips: { + enabled: true, + custom: null, + mode: 'nearest', + position: 'average', + intersect: true, + backgroundColor: 'rgba(0,0,0,0.8)', + titleFontStyle: 'bold', + titleSpacing: 2, + titleMarginBottom: 6, + titleFontColor: '#fff', + titleAlign: 'left', + bodySpacing: 2, + bodyFontColor: '#fff', + bodyAlign: 'left', + footerFontStyle: 'bold', + footerSpacing: 2, + footerMarginTop: 6, + footerFontColor: '#fff', + footerAlign: 'left', + yPadding: 6, + xPadding: 6, + caretPadding: 2, + caretSize: 5, + cornerRadius: 6, + multiKeyBackground: '#fff', + displayColors: true, + borderColor: 'rgba(0,0,0,0)', + borderWidth: 0, + callbacks: { + // Args are: (tooltipItems, data) + beforeTitle: helpers.noop, + title: function(tooltipItems, data) { + // Pick first xLabel for now + var title = ''; + var labels = data.labels; + var labelCount = labels ? labels.length : 0; + + if (tooltipItems.length > 0) { + var item = tooltipItems[0]; + + if (item.xLabel) { + title = item.xLabel; + } else if (labelCount > 0 && item.index < labelCount) { + title = labels[item.index]; + } + } + + return title; + }, + afterTitle: helpers.noop, + + // Args are: (tooltipItems, data) + beforeBody: helpers.noop, + + // Args are: (tooltipItem, data) + beforeLabel: helpers.noop, + label: function(tooltipItem, data) { + var label = data.datasets[tooltipItem.datasetIndex].label || ''; + + if (label) { + label += ': '; + } + label += tooltipItem.yLabel; + return label; + }, + labelColor: function(tooltipItem, chart) { + var meta = chart.getDatasetMeta(tooltipItem.datasetIndex); + var activeElement = meta.data[tooltipItem.index]; + var view = activeElement._view; + return { + borderColor: view.borderColor, + backgroundColor: view.backgroundColor + }; + }, + labelTextColor: function() { + return this._options.bodyFontColor; + }, + afterLabel: helpers.noop, + + // Args are: (tooltipItems, data) + afterBody: helpers.noop, + + // Args are: (tooltipItems, data) + beforeFooter: helpers.noop, + footer: helpers.noop, + afterFooter: helpers.noop + } + } +}); + +module.exports = function(Chart) { + + /** + * Helper method to merge the opacity into a color + */ + function mergeOpacity(colorString, opacity) { + var color = helpers.color(colorString); + return color.alpha(opacity * color.alpha()).rgbaString(); + } + + // Helper to push or concat based on if the 2nd parameter is an array or not + function pushOrConcat(base, toPush) { + if (toPush) { + if (helpers.isArray(toPush)) { + // base = base.concat(toPush); + Array.prototype.push.apply(base, toPush); + } else { + base.push(toPush); + } + } + + return base; + } + + // Private helper to create a tooltip item model + // @param element : the chart element (point, arc, bar) to create the tooltip item for + // @return : new tooltip item + function createTooltipItem(element) { + var xScale = element._xScale; + var yScale = element._yScale || element._scale; // handle radar || polarArea charts + var index = element._index; + var datasetIndex = element._datasetIndex; + + return { + xLabel: xScale ? xScale.getLabelForIndex(index, datasetIndex) : '', + yLabel: yScale ? yScale.getLabelForIndex(index, datasetIndex) : '', + index: index, + datasetIndex: datasetIndex, + x: element._model.x, + y: element._model.y + }; + } + + /** + * Helper to get the reset model for the tooltip + * @param tooltipOpts {Object} the tooltip options + */ + function getBaseModel(tooltipOpts) { + var globalDefaults = defaults.global; + var valueOrDefault = helpers.valueOrDefault; + + return { + // Positioning + xPadding: tooltipOpts.xPadding, + yPadding: tooltipOpts.yPadding, + xAlign: tooltipOpts.xAlign, + yAlign: tooltipOpts.yAlign, + + // Body + bodyFontColor: tooltipOpts.bodyFontColor, + _bodyFontFamily: valueOrDefault(tooltipOpts.bodyFontFamily, globalDefaults.defaultFontFamily), + _bodyFontStyle: valueOrDefault(tooltipOpts.bodyFontStyle, globalDefaults.defaultFontStyle), + _bodyAlign: tooltipOpts.bodyAlign, + bodyFontSize: valueOrDefault(tooltipOpts.bodyFontSize, globalDefaults.defaultFontSize), + bodySpacing: tooltipOpts.bodySpacing, + + // Title + titleFontColor: tooltipOpts.titleFontColor, + _titleFontFamily: valueOrDefault(tooltipOpts.titleFontFamily, globalDefaults.defaultFontFamily), + _titleFontStyle: valueOrDefault(tooltipOpts.titleFontStyle, globalDefaults.defaultFontStyle), + titleFontSize: valueOrDefault(tooltipOpts.titleFontSize, globalDefaults.defaultFontSize), + _titleAlign: tooltipOpts.titleAlign, + titleSpacing: tooltipOpts.titleSpacing, + titleMarginBottom: tooltipOpts.titleMarginBottom, + + // Footer + footerFontColor: tooltipOpts.footerFontColor, + _footerFontFamily: valueOrDefault(tooltipOpts.footerFontFamily, globalDefaults.defaultFontFamily), + _footerFontStyle: valueOrDefault(tooltipOpts.footerFontStyle, globalDefaults.defaultFontStyle), + footerFontSize: valueOrDefault(tooltipOpts.footerFontSize, globalDefaults.defaultFontSize), + _footerAlign: tooltipOpts.footerAlign, + footerSpacing: tooltipOpts.footerSpacing, + footerMarginTop: tooltipOpts.footerMarginTop, + + // Appearance + caretSize: tooltipOpts.caretSize, + cornerRadius: tooltipOpts.cornerRadius, + backgroundColor: tooltipOpts.backgroundColor, + opacity: 0, + legendColorBackground: tooltipOpts.multiKeyBackground, + displayColors: tooltipOpts.displayColors, + borderColor: tooltipOpts.borderColor, + borderWidth: tooltipOpts.borderWidth + }; + } + + /** + * Get the size of the tooltip + */ + function getTooltipSize(tooltip, model) { + var ctx = tooltip._chart.ctx; + + var height = model.yPadding * 2; // Tooltip Padding + var width = 0; + + // Count of all lines in the body + var body = model.body; + var combinedBodyLength = body.reduce(function(count, bodyItem) { + return count + bodyItem.before.length + bodyItem.lines.length + bodyItem.after.length; + }, 0); + combinedBodyLength += model.beforeBody.length + model.afterBody.length; + + var titleLineCount = model.title.length; + var footerLineCount = model.footer.length; + var titleFontSize = model.titleFontSize; + var bodyFontSize = model.bodyFontSize; + var footerFontSize = model.footerFontSize; + + height += titleLineCount * titleFontSize; // Title Lines + height += titleLineCount ? (titleLineCount - 1) * model.titleSpacing : 0; // Title Line Spacing + height += titleLineCount ? model.titleMarginBottom : 0; // Title's bottom Margin + height += combinedBodyLength * bodyFontSize; // Body Lines + height += combinedBodyLength ? (combinedBodyLength - 1) * model.bodySpacing : 0; // Body Line Spacing + height += footerLineCount ? model.footerMarginTop : 0; // Footer Margin + height += footerLineCount * (footerFontSize); // Footer Lines + height += footerLineCount ? (footerLineCount - 1) * model.footerSpacing : 0; // Footer Line Spacing + + // Title width + var widthPadding = 0; + var maxLineWidth = function(line) { + width = Math.max(width, ctx.measureText(line).width + widthPadding); + }; + + ctx.font = helpers.fontString(titleFontSize, model._titleFontStyle, model._titleFontFamily); + helpers.each(model.title, maxLineWidth); + + // Body width + ctx.font = helpers.fontString(bodyFontSize, model._bodyFontStyle, model._bodyFontFamily); + helpers.each(model.beforeBody.concat(model.afterBody), maxLineWidth); + + // Body lines may include some extra width due to the color box + widthPadding = model.displayColors ? (bodyFontSize + 2) : 0; + helpers.each(body, function(bodyItem) { + helpers.each(bodyItem.before, maxLineWidth); + helpers.each(bodyItem.lines, maxLineWidth); + helpers.each(bodyItem.after, maxLineWidth); + }); + + // Reset back to 0 + widthPadding = 0; + + // Footer width + ctx.font = helpers.fontString(footerFontSize, model._footerFontStyle, model._footerFontFamily); + helpers.each(model.footer, maxLineWidth); + + // Add padding + width += 2 * model.xPadding; + + return { + width: width, + height: height + }; + } + + /** + * Helper to get the alignment of a tooltip given the size + */ + function determineAlignment(tooltip, size) { + var model = tooltip._model; + var chart = tooltip._chart; + var chartArea = tooltip._chart.chartArea; + var xAlign = 'center'; + var yAlign = 'center'; + + if (model.y < size.height) { + yAlign = 'top'; + } else if (model.y > (chart.height - size.height)) { + yAlign = 'bottom'; + } + + var lf, rf; // functions to determine left, right alignment + var olf, orf; // functions to determine if left/right alignment causes tooltip to go outside chart + var yf; // function to get the y alignment if the tooltip goes outside of the left or right edges + var midX = (chartArea.left + chartArea.right) / 2; + var midY = (chartArea.top + chartArea.bottom) / 2; + + if (yAlign === 'center') { + lf = function(x) { + return x <= midX; + }; + rf = function(x) { + return x > midX; + }; + } else { + lf = function(x) { + return x <= (size.width / 2); + }; + rf = function(x) { + return x >= (chart.width - (size.width / 2)); + }; + } + + olf = function(x) { + return x + size.width > chart.width; + }; + orf = function(x) { + return x - size.width < 0; + }; + yf = function(y) { + return y <= midY ? 'top' : 'bottom'; + }; + + if (lf(model.x)) { + xAlign = 'left'; + + // Is tooltip too wide and goes over the right side of the chart.? + if (olf(model.x)) { + xAlign = 'center'; + yAlign = yf(model.y); + } + } else if (rf(model.x)) { + xAlign = 'right'; + + // Is tooltip too wide and goes outside left edge of canvas? + if (orf(model.x)) { + xAlign = 'center'; + yAlign = yf(model.y); + } + } + + var opts = tooltip._options; + return { + xAlign: opts.xAlign ? opts.xAlign : xAlign, + yAlign: opts.yAlign ? opts.yAlign : yAlign + }; + } + + /** + * @Helper to get the location a tooltip needs to be placed at given the initial position (via the vm) and the size and alignment + */ + function getBackgroundPoint(vm, size, alignment) { + // Background Position + var x = vm.x; + var y = vm.y; + + var caretSize = vm.caretSize; + var caretPadding = vm.caretPadding; + var cornerRadius = vm.cornerRadius; + var xAlign = alignment.xAlign; + var yAlign = alignment.yAlign; + var paddingAndSize = caretSize + caretPadding; + var radiusAndPadding = cornerRadius + caretPadding; + + if (xAlign === 'right') { + x -= size.width; + } else if (xAlign === 'center') { + x -= (size.width / 2); + } + + if (yAlign === 'top') { + y += paddingAndSize; + } else if (yAlign === 'bottom') { + y -= size.height + paddingAndSize; + } else { + y -= (size.height / 2); + } + + if (yAlign === 'center') { + if (xAlign === 'left') { + x += paddingAndSize; + } else if (xAlign === 'right') { + x -= paddingAndSize; + } + } else if (xAlign === 'left') { + x -= radiusAndPadding; + } else if (xAlign === 'right') { + x += radiusAndPadding; + } + + return { + x: x, + y: y + }; + } + + Chart.Tooltip = Element.extend({ + initialize: function() { + this._model = getBaseModel(this._options); + this._lastActive = []; + }, + + // Get the title + // Args are: (tooltipItem, data) + getTitle: function() { + var me = this; + var opts = me._options; + var callbacks = opts.callbacks; + + var beforeTitle = callbacks.beforeTitle.apply(me, arguments); + var title = callbacks.title.apply(me, arguments); + var afterTitle = callbacks.afterTitle.apply(me, arguments); + + var lines = []; + lines = pushOrConcat(lines, beforeTitle); + lines = pushOrConcat(lines, title); + lines = pushOrConcat(lines, afterTitle); + + return lines; + }, + + // Args are: (tooltipItem, data) + getBeforeBody: function() { + var lines = this._options.callbacks.beforeBody.apply(this, arguments); + return helpers.isArray(lines) ? lines : lines !== undefined ? [lines] : []; + }, + + // Args are: (tooltipItem, data) + getBody: function(tooltipItems, data) { + var me = this; + var callbacks = me._options.callbacks; + var bodyItems = []; + + helpers.each(tooltipItems, function(tooltipItem) { + var bodyItem = { + before: [], + lines: [], + after: [] + }; + pushOrConcat(bodyItem.before, callbacks.beforeLabel.call(me, tooltipItem, data)); + pushOrConcat(bodyItem.lines, callbacks.label.call(me, tooltipItem, data)); + pushOrConcat(bodyItem.after, callbacks.afterLabel.call(me, tooltipItem, data)); + + bodyItems.push(bodyItem); + }); + + return bodyItems; + }, + + // Args are: (tooltipItem, data) + getAfterBody: function() { + var lines = this._options.callbacks.afterBody.apply(this, arguments); + return helpers.isArray(lines) ? lines : lines !== undefined ? [lines] : []; + }, + + // Get the footer and beforeFooter and afterFooter lines + // Args are: (tooltipItem, data) + getFooter: function() { + var me = this; + var callbacks = me._options.callbacks; + + var beforeFooter = callbacks.beforeFooter.apply(me, arguments); + var footer = callbacks.footer.apply(me, arguments); + var afterFooter = callbacks.afterFooter.apply(me, arguments); + + var lines = []; + lines = pushOrConcat(lines, beforeFooter); + lines = pushOrConcat(lines, footer); + lines = pushOrConcat(lines, afterFooter); + + return lines; + }, + + update: function(changed) { + var me = this; + var opts = me._options; + + // Need to regenerate the model because its faster than using extend and it is necessary due to the optimization in Chart.Element.transition + // that does _view = _model if ease === 1. This causes the 2nd tooltip update to set properties in both the view and model at the same time + // which breaks any animations. + var existingModel = me._model; + var model = me._model = getBaseModel(opts); + var active = me._active; + + var data = me._data; + + // In the case where active.length === 0 we need to keep these at existing values for good animations + var alignment = { + xAlign: existingModel.xAlign, + yAlign: existingModel.yAlign + }; + var backgroundPoint = { + x: existingModel.x, + y: existingModel.y + }; + var tooltipSize = { + width: existingModel.width, + height: existingModel.height + }; + var tooltipPosition = { + x: existingModel.caretX, + y: existingModel.caretY + }; + + var i, len; + + if (active.length) { + model.opacity = 1; + + var labelColors = []; + var labelTextColors = []; + tooltipPosition = Chart.Tooltip.positioners[opts.position].call(me, active, me._eventPosition); + + var tooltipItems = []; + for (i = 0, len = active.length; i < len; ++i) { + tooltipItems.push(createTooltipItem(active[i])); + } + + // If the user provided a filter function, use it to modify the tooltip items + if (opts.filter) { + tooltipItems = tooltipItems.filter(function(a) { + return opts.filter(a, data); + }); + } + + // If the user provided a sorting function, use it to modify the tooltip items + if (opts.itemSort) { + tooltipItems = tooltipItems.sort(function(a, b) { + return opts.itemSort(a, b, data); + }); + } + + // Determine colors for boxes + helpers.each(tooltipItems, function(tooltipItem) { + labelColors.push(opts.callbacks.labelColor.call(me, tooltipItem, me._chart)); + labelTextColors.push(opts.callbacks.labelTextColor.call(me, tooltipItem, me._chart)); + }); + + + // Build the Text Lines + model.title = me.getTitle(tooltipItems, data); + model.beforeBody = me.getBeforeBody(tooltipItems, data); + model.body = me.getBody(tooltipItems, data); + model.afterBody = me.getAfterBody(tooltipItems, data); + model.footer = me.getFooter(tooltipItems, data); + + // Initial positioning and colors + model.x = Math.round(tooltipPosition.x); + model.y = Math.round(tooltipPosition.y); + model.caretPadding = opts.caretPadding; + model.labelColors = labelColors; + model.labelTextColors = labelTextColors; + + // data points + model.dataPoints = tooltipItems; + + // We need to determine alignment of the tooltip + tooltipSize = getTooltipSize(this, model); + alignment = determineAlignment(this, tooltipSize); + // Final Size and Position + backgroundPoint = getBackgroundPoint(model, tooltipSize, alignment); + } else { + model.opacity = 0; + } + + model.xAlign = alignment.xAlign; + model.yAlign = alignment.yAlign; + model.x = backgroundPoint.x; + model.y = backgroundPoint.y; + model.width = tooltipSize.width; + model.height = tooltipSize.height; + + // Point where the caret on the tooltip points to + model.caretX = tooltipPosition.x; + model.caretY = tooltipPosition.y; + + me._model = model; + + if (changed && opts.custom) { + opts.custom.call(me, model); + } + + return me; + }, + drawCaret: function(tooltipPoint, size) { + var ctx = this._chart.ctx; + var vm = this._view; + var caretPosition = this.getCaretPosition(tooltipPoint, size, vm); + + ctx.lineTo(caretPosition.x1, caretPosition.y1); + ctx.lineTo(caretPosition.x2, caretPosition.y2); + ctx.lineTo(caretPosition.x3, caretPosition.y3); + }, + getCaretPosition: function(tooltipPoint, size, vm) { + var x1, x2, x3, y1, y2, y3; + var caretSize = vm.caretSize; + var cornerRadius = vm.cornerRadius; + var xAlign = vm.xAlign; + var yAlign = vm.yAlign; + var ptX = tooltipPoint.x; + var ptY = tooltipPoint.y; + var width = size.width; + var height = size.height; + + if (yAlign === 'center') { + y2 = ptY + (height / 2); + + if (xAlign === 'left') { + x1 = ptX; + x2 = x1 - caretSize; + x3 = x1; + + y1 = y2 + caretSize; + y3 = y2 - caretSize; + } else { + x1 = ptX + width; + x2 = x1 + caretSize; + x3 = x1; + + y1 = y2 - caretSize; + y3 = y2 + caretSize; + } + } else { + if (xAlign === 'left') { + x2 = ptX + cornerRadius + (caretSize); + x1 = x2 - caretSize; + x3 = x2 + caretSize; + } else if (xAlign === 'right') { + x2 = ptX + width - cornerRadius - caretSize; + x1 = x2 - caretSize; + x3 = x2 + caretSize; + } else { + x2 = ptX + (width / 2); + x1 = x2 - caretSize; + x3 = x2 + caretSize; + } + if (yAlign === 'top') { + y1 = ptY; + y2 = y1 - caretSize; + y3 = y1; + } else { + y1 = ptY + height; + y2 = y1 + caretSize; + y3 = y1; + // invert drawing order + var tmp = x3; + x3 = x1; + x1 = tmp; + } + } + return {x1: x1, x2: x2, x3: x3, y1: y1, y2: y2, y3: y3}; + }, + drawTitle: function(pt, vm, ctx, opacity) { + var title = vm.title; + + if (title.length) { + ctx.textAlign = vm._titleAlign; + ctx.textBaseline = 'top'; + + var titleFontSize = vm.titleFontSize; + var titleSpacing = vm.titleSpacing; + + ctx.fillStyle = mergeOpacity(vm.titleFontColor, opacity); + ctx.font = helpers.fontString(titleFontSize, vm._titleFontStyle, vm._titleFontFamily); + + var i, len; + for (i = 0, len = title.length; i < len; ++i) { + ctx.fillText(title[i], pt.x, pt.y); + pt.y += titleFontSize + titleSpacing; // Line Height and spacing + + if (i + 1 === title.length) { + pt.y += vm.titleMarginBottom - titleSpacing; // If Last, add margin, remove spacing + } + } + } + }, + drawBody: function(pt, vm, ctx, opacity) { + var bodyFontSize = vm.bodyFontSize; + var bodySpacing = vm.bodySpacing; + var body = vm.body; + + ctx.textAlign = vm._bodyAlign; + ctx.textBaseline = 'top'; + ctx.font = helpers.fontString(bodyFontSize, vm._bodyFontStyle, vm._bodyFontFamily); + + // Before Body + var xLinePadding = 0; + var fillLineOfText = function(line) { + ctx.fillText(line, pt.x + xLinePadding, pt.y); + pt.y += bodyFontSize + bodySpacing; + }; + + // Before body lines + ctx.fillStyle = mergeOpacity(vm.bodyFontColor, opacity); + helpers.each(vm.beforeBody, fillLineOfText); + + var drawColorBoxes = vm.displayColors; + xLinePadding = drawColorBoxes ? (bodyFontSize + 2) : 0; + + // Draw body lines now + helpers.each(body, function(bodyItem, i) { + var textColor = mergeOpacity(vm.labelTextColors[i], opacity); + ctx.fillStyle = textColor; + helpers.each(bodyItem.before, fillLineOfText); + + helpers.each(bodyItem.lines, function(line) { + // Draw Legend-like boxes if needed + if (drawColorBoxes) { + // Fill a white rect so that colours merge nicely if the opacity is < 1 + ctx.fillStyle = mergeOpacity(vm.legendColorBackground, opacity); + ctx.fillRect(pt.x, pt.y, bodyFontSize, bodyFontSize); + + // Border + ctx.lineWidth = 1; + ctx.strokeStyle = mergeOpacity(vm.labelColors[i].borderColor, opacity); + ctx.strokeRect(pt.x, pt.y, bodyFontSize, bodyFontSize); + + // Inner square + ctx.fillStyle = mergeOpacity(vm.labelColors[i].backgroundColor, opacity); + ctx.fillRect(pt.x + 1, pt.y + 1, bodyFontSize - 2, bodyFontSize - 2); + ctx.fillStyle = textColor; + } + + fillLineOfText(line); + }); + + helpers.each(bodyItem.after, fillLineOfText); + }); + + // Reset back to 0 for after body + xLinePadding = 0; + + // After body lines + helpers.each(vm.afterBody, fillLineOfText); + pt.y -= bodySpacing; // Remove last body spacing + }, + drawFooter: function(pt, vm, ctx, opacity) { + var footer = vm.footer; + + if (footer.length) { + pt.y += vm.footerMarginTop; + + ctx.textAlign = vm._footerAlign; + ctx.textBaseline = 'top'; + + ctx.fillStyle = mergeOpacity(vm.footerFontColor, opacity); + ctx.font = helpers.fontString(vm.footerFontSize, vm._footerFontStyle, vm._footerFontFamily); + + helpers.each(footer, function(line) { + ctx.fillText(line, pt.x, pt.y); + pt.y += vm.footerFontSize + vm.footerSpacing; + }); + } + }, + drawBackground: function(pt, vm, ctx, tooltipSize, opacity) { + ctx.fillStyle = mergeOpacity(vm.backgroundColor, opacity); + ctx.strokeStyle = mergeOpacity(vm.borderColor, opacity); + ctx.lineWidth = vm.borderWidth; + var xAlign = vm.xAlign; + var yAlign = vm.yAlign; + var x = pt.x; + var y = pt.y; + var width = tooltipSize.width; + var height = tooltipSize.height; + var radius = vm.cornerRadius; + + ctx.beginPath(); + ctx.moveTo(x + radius, y); + if (yAlign === 'top') { + this.drawCaret(pt, tooltipSize); + } + ctx.lineTo(x + width - radius, y); + ctx.quadraticCurveTo(x + width, y, x + width, y + radius); + if (yAlign === 'center' && xAlign === 'right') { + this.drawCaret(pt, tooltipSize); + } + ctx.lineTo(x + width, y + height - radius); + ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height); + if (yAlign === 'bottom') { + this.drawCaret(pt, tooltipSize); + } + ctx.lineTo(x + radius, y + height); + ctx.quadraticCurveTo(x, y + height, x, y + height - radius); + if (yAlign === 'center' && xAlign === 'left') { + this.drawCaret(pt, tooltipSize); + } + ctx.lineTo(x, y + radius); + ctx.quadraticCurveTo(x, y, x + radius, y); + ctx.closePath(); + + ctx.fill(); + + if (vm.borderWidth > 0) { + ctx.stroke(); + } + }, + draw: function() { + var ctx = this._chart.ctx; + var vm = this._view; + + if (vm.opacity === 0) { + return; + } + + var tooltipSize = { + width: vm.width, + height: vm.height + }; + var pt = { + x: vm.x, + y: vm.y + }; + + // IE11/Edge does not like very small opacities, so snap to 0 + var opacity = Math.abs(vm.opacity < 1e-3) ? 0 : vm.opacity; + + // Truthy/falsey value for empty tooltip + var hasTooltipContent = vm.title.length || vm.beforeBody.length || vm.body.length || vm.afterBody.length || vm.footer.length; + + if (this._options.enabled && hasTooltipContent) { + // Draw Background + this.drawBackground(pt, vm, ctx, tooltipSize, opacity); + + // Draw Title, Body, and Footer + pt.x += vm.xPadding; + pt.y += vm.yPadding; + + // Titles + this.drawTitle(pt, vm, ctx, opacity); + + // Body + this.drawBody(pt, vm, ctx, opacity); + + // Footer + this.drawFooter(pt, vm, ctx, opacity); + } + }, + + /** + * Handle an event + * @private + * @param {IEvent} event - The event to handle + * @returns {Boolean} true if the tooltip changed + */ + handleEvent: function(e) { + var me = this; + var options = me._options; + var changed = false; + + me._lastActive = me._lastActive || []; + + // Find Active Elements for tooltips + if (e.type === 'mouseout') { + me._active = []; + } else { + me._active = me._chart.getElementsAtEventForMode(e, options.mode, options); + } + + // Remember Last Actives + changed = !helpers.arrayEquals(me._active, me._lastActive); + + // If tooltip didn't change, do not handle the target event + if (!changed) { + return false; + } + + me._lastActive = me._active; + + if (options.enabled || options.custom) { + me._eventPosition = { + x: e.x, + y: e.y + }; + + var model = me._model; + me.update(true); + me.pivot(); + + // See if our tooltip position changed + changed |= (model.x !== me._model.x) || (model.y !== me._model.y); + } + + return changed; + } + }); + + /** + * @namespace Chart.Tooltip.positioners + */ + Chart.Tooltip.positioners = { + /** + * Average mode places the tooltip at the average position of the elements shown + * @function Chart.Tooltip.positioners.average + * @param elements {ChartElement[]} the elements being displayed in the tooltip + * @returns {Point} tooltip position + */ + average: function(elements) { + if (!elements.length) { + return false; + } + + var i, len; + var x = 0; + var y = 0; + var count = 0; + + for (i = 0, len = elements.length; i < len; ++i) { + var el = elements[i]; + if (el && el.hasValue()) { + var pos = el.tooltipPosition(); + x += pos.x; + y += pos.y; + ++count; + } + } + + return { + x: Math.round(x / count), + y: Math.round(y / count) + }; + }, + + /** + * Gets the tooltip position nearest of the item nearest to the event position + * @function Chart.Tooltip.positioners.nearest + * @param elements {Chart.Element[]} the tooltip elements + * @param eventPosition {Point} the position of the event in canvas coordinates + * @returns {Point} the tooltip position + */ + nearest: function(elements, eventPosition) { + var x = eventPosition.x; + var y = eventPosition.y; + var minDistance = Number.POSITIVE_INFINITY; + var i, len, nearestElement; + + for (i = 0, len = elements.length; i < len; ++i) { + var el = elements[i]; + if (el && el.hasValue()) { + var center = el.getCenterPoint(); + var d = helpers.distanceBetweenPoints(eventPosition, center); + + if (d < minDistance) { + minDistance = d; + nearestElement = el; + } + } + } + + if (nearestElement) { + var tp = nearestElement.tooltipPosition(); + x = tp.x; + y = tp.y; + } + + return { + x: x, + y: y + }; + } + }; +}; + +},{"25":25,"26":26,"45":45}],36:[function(require,module,exports){ +'use strict'; + +var defaults = require(25); +var Element = require(26); +var helpers = require(45); + +defaults._set('global', { + elements: { + arc: { + backgroundColor: defaults.global.defaultColor, + borderColor: '#fff', + borderWidth: 2 + } + } +}); + +module.exports = Element.extend({ + inLabelRange: function(mouseX) { + var vm = this._view; + + if (vm) { + return (Math.pow(mouseX - vm.x, 2) < Math.pow(vm.radius + vm.hoverRadius, 2)); + } + return false; + }, + + inRange: function(chartX, chartY) { + var vm = this._view; + + if (vm) { + var pointRelativePosition = helpers.getAngleFromPoint(vm, {x: chartX, y: chartY}); + var angle = pointRelativePosition.angle; + var distance = pointRelativePosition.distance; + + // Sanitise angle range + var startAngle = vm.startAngle; + var endAngle = vm.endAngle; + while (endAngle < startAngle) { + endAngle += 2.0 * Math.PI; + } + while (angle > endAngle) { + angle -= 2.0 * Math.PI; + } + while (angle < startAngle) { + angle += 2.0 * Math.PI; + } + + // Check if within the range of the open/close angle + var betweenAngles = (angle >= startAngle && angle <= endAngle); + var withinRadius = (distance >= vm.innerRadius && distance <= vm.outerRadius); + + return (betweenAngles && withinRadius); + } + return false; + }, + + getCenterPoint: function() { + var vm = this._view; + var halfAngle = (vm.startAngle + vm.endAngle) / 2; + var halfRadius = (vm.innerRadius + vm.outerRadius) / 2; + return { + x: vm.x + Math.cos(halfAngle) * halfRadius, + y: vm.y + Math.sin(halfAngle) * halfRadius + }; + }, + + getArea: function() { + var vm = this._view; + return Math.PI * ((vm.endAngle - vm.startAngle) / (2 * Math.PI)) * (Math.pow(vm.outerRadius, 2) - Math.pow(vm.innerRadius, 2)); + }, + + tooltipPosition: function() { + var vm = this._view; + var centreAngle = vm.startAngle + ((vm.endAngle - vm.startAngle) / 2); + var rangeFromCentre = (vm.outerRadius - vm.innerRadius) / 2 + vm.innerRadius; + + return { + x: vm.x + (Math.cos(centreAngle) * rangeFromCentre), + y: vm.y + (Math.sin(centreAngle) * rangeFromCentre) + }; + }, + + draw: function() { + var ctx = this._chart.ctx; + var vm = this._view; + var sA = vm.startAngle; + var eA = vm.endAngle; + + ctx.beginPath(); + + ctx.arc(vm.x, vm.y, vm.outerRadius, sA, eA); + ctx.arc(vm.x, vm.y, vm.innerRadius, eA, sA, true); + + ctx.closePath(); + ctx.strokeStyle = vm.borderColor; + ctx.lineWidth = vm.borderWidth; + + ctx.fillStyle = vm.backgroundColor; + + ctx.fill(); + ctx.lineJoin = 'bevel'; + + if (vm.borderWidth) { + ctx.stroke(); + } + } +}); + +},{"25":25,"26":26,"45":45}],37:[function(require,module,exports){ +'use strict'; + +var defaults = require(25); +var Element = require(26); +var helpers = require(45); + +var globalDefaults = defaults.global; + +defaults._set('global', { + elements: { + line: { + tension: 0.4, + backgroundColor: globalDefaults.defaultColor, + borderWidth: 3, + borderColor: globalDefaults.defaultColor, + borderCapStyle: 'butt', + borderDash: [], + borderDashOffset: 0.0, + borderJoinStyle: 'miter', + capBezierPoints: true, + fill: true, // do we fill in the area between the line and its base axis + } + } +}); + +module.exports = Element.extend({ + draw: function() { + var me = this; + var vm = me._view; + var ctx = me._chart.ctx; + var spanGaps = vm.spanGaps; + var points = me._children.slice(); // clone array + var globalOptionLineElements = globalDefaults.elements.line; + var lastDrawnIndex = -1; + var index, current, previous, currentVM; + + // If we are looping, adding the first point again + if (me._loop && points.length) { + points.push(points[0]); + } + + ctx.save(); + + // Stroke Line Options + ctx.lineCap = vm.borderCapStyle || globalOptionLineElements.borderCapStyle; + + // IE 9 and 10 do not support line dash + if (ctx.setLineDash) { + ctx.setLineDash(vm.borderDash || globalOptionLineElements.borderDash); + } + + ctx.lineDashOffset = vm.borderDashOffset || globalOptionLineElements.borderDashOffset; + ctx.lineJoin = vm.borderJoinStyle || globalOptionLineElements.borderJoinStyle; + ctx.lineWidth = vm.borderWidth || globalOptionLineElements.borderWidth; + ctx.strokeStyle = vm.borderColor || globalDefaults.defaultColor; + + // Stroke Line + ctx.beginPath(); + lastDrawnIndex = -1; + + for (index = 0; index < points.length; ++index) { + current = points[index]; + previous = helpers.previousItem(points, index); + currentVM = current._view; + + // First point moves to it's starting position no matter what + if (index === 0) { + if (!currentVM.skip) { + ctx.moveTo(currentVM.x, currentVM.y); + lastDrawnIndex = index; + } + } else { + previous = lastDrawnIndex === -1 ? previous : points[lastDrawnIndex]; + + if (!currentVM.skip) { + if ((lastDrawnIndex !== (index - 1) && !spanGaps) || lastDrawnIndex === -1) { + // There was a gap and this is the first point after the gap + ctx.moveTo(currentVM.x, currentVM.y); + } else { + // Line to next point + helpers.canvas.lineTo(ctx, previous._view, current._view); + } + lastDrawnIndex = index; + } + } + } + + ctx.stroke(); + ctx.restore(); + } +}); + +},{"25":25,"26":26,"45":45}],38:[function(require,module,exports){ +'use strict'; + +var defaults = require(25); +var Element = require(26); +var helpers = require(45); + +var defaultColor = defaults.global.defaultColor; + +defaults._set('global', { + elements: { + point: { + radius: 3, + pointStyle: 'circle', + backgroundColor: defaultColor, + borderColor: defaultColor, + borderWidth: 1, + // Hover + hitRadius: 1, + hoverRadius: 4, + hoverBorderWidth: 1 + } + } +}); + +function xRange(mouseX) { + var vm = this._view; + return vm ? (Math.pow(mouseX - vm.x, 2) < Math.pow(vm.radius + vm.hitRadius, 2)) : false; +} + +function yRange(mouseY) { + var vm = this._view; + return vm ? (Math.pow(mouseY - vm.y, 2) < Math.pow(vm.radius + vm.hitRadius, 2)) : false; +} + +module.exports = Element.extend({ + inRange: function(mouseX, mouseY) { + var vm = this._view; + return vm ? ((Math.pow(mouseX - vm.x, 2) + Math.pow(mouseY - vm.y, 2)) < Math.pow(vm.hitRadius + vm.radius, 2)) : false; + }, + + inLabelRange: xRange, + inXRange: xRange, + inYRange: yRange, + + getCenterPoint: function() { + var vm = this._view; + return { + x: vm.x, + y: vm.y + }; + }, + + getArea: function() { + return Math.PI * Math.pow(this._view.radius, 2); + }, + + tooltipPosition: function() { + var vm = this._view; + return { + x: vm.x, + y: vm.y, + padding: vm.radius + vm.borderWidth + }; + }, + + draw: function(chartArea) { + var vm = this._view; + var model = this._model; + var ctx = this._chart.ctx; + var pointStyle = vm.pointStyle; + var radius = vm.radius; + var x = vm.x; + var y = vm.y; + var color = helpers.color; + var errMargin = 1.01; // 1.01 is margin for Accumulated error. (Especially Edge, IE.) + var ratio = 0; + + if (vm.skip) { + return; + } + + ctx.strokeStyle = vm.borderColor || defaultColor; + ctx.lineWidth = helpers.valueOrDefault(vm.borderWidth, defaults.global.elements.point.borderWidth); + ctx.fillStyle = vm.backgroundColor || defaultColor; + + // Cliping for Points. + // going out from inner charArea? + if ((chartArea !== undefined) && ((model.x < chartArea.left) || (chartArea.right * errMargin < model.x) || (model.y < chartArea.top) || (chartArea.bottom * errMargin < model.y))) { + // Point fade out + if (model.x < chartArea.left) { + ratio = (x - model.x) / (chartArea.left - model.x); + } else if (chartArea.right * errMargin < model.x) { + ratio = (model.x - x) / (model.x - chartArea.right); + } else if (model.y < chartArea.top) { + ratio = (y - model.y) / (chartArea.top - model.y); + } else if (chartArea.bottom * errMargin < model.y) { + ratio = (model.y - y) / (model.y - chartArea.bottom); + } + ratio = Math.round(ratio * 100) / 100; + ctx.strokeStyle = color(ctx.strokeStyle).alpha(ratio).rgbString(); + ctx.fillStyle = color(ctx.fillStyle).alpha(ratio).rgbString(); + } + + helpers.canvas.drawPoint(ctx, pointStyle, radius, x, y); + } +}); + +},{"25":25,"26":26,"45":45}],39:[function(require,module,exports){ +'use strict'; + +var defaults = require(25); +var Element = require(26); + +defaults._set('global', { + elements: { + rectangle: { + backgroundColor: defaults.global.defaultColor, + borderColor: defaults.global.defaultColor, + borderSkipped: 'bottom', + borderWidth: 0 + } + } +}); + +function isVertical(bar) { + return bar._view.width !== undefined; +} + +/** + * Helper function to get the bounds of the bar regardless of the orientation + * @param bar {Chart.Element.Rectangle} the bar + * @return {Bounds} bounds of the bar + * @private + */ +function getBarBounds(bar) { + var vm = bar._view; + var x1, x2, y1, y2; + + if (isVertical(bar)) { + // vertical + var halfWidth = vm.width / 2; + x1 = vm.x - halfWidth; + x2 = vm.x + halfWidth; + y1 = Math.min(vm.y, vm.base); + y2 = Math.max(vm.y, vm.base); + } else { + // horizontal bar + var halfHeight = vm.height / 2; + x1 = Math.min(vm.x, vm.base); + x2 = Math.max(vm.x, vm.base); + y1 = vm.y - halfHeight; + y2 = vm.y + halfHeight; + } + + return { + left: x1, + top: y1, + right: x2, + bottom: y2 + }; +} + +module.exports = Element.extend({ + draw: function() { + var ctx = this._chart.ctx; + var vm = this._view; + var left, right, top, bottom, signX, signY, borderSkipped; + var borderWidth = vm.borderWidth; + + if (!vm.horizontal) { + // bar + left = vm.x - vm.width / 2; + right = vm.x + vm.width / 2; + top = vm.y; + bottom = vm.base; + signX = 1; + signY = bottom > top ? 1 : -1; + borderSkipped = vm.borderSkipped || 'bottom'; + } else { + // horizontal bar + left = vm.base; + right = vm.x; + top = vm.y - vm.height / 2; + bottom = vm.y + vm.height / 2; + signX = right > left ? 1 : -1; + signY = 1; + borderSkipped = vm.borderSkipped || 'left'; + } + + // Canvas doesn't allow us to stroke inside the width so we can + // adjust the sizes to fit if we're setting a stroke on the line + if (borderWidth) { + // borderWidth shold be less than bar width and bar height. + var barSize = Math.min(Math.abs(left - right), Math.abs(top - bottom)); + borderWidth = borderWidth > barSize ? barSize : borderWidth; + var halfStroke = borderWidth / 2; + // Adjust borderWidth when bar top position is near vm.base(zero). + var borderLeft = left + (borderSkipped !== 'left' ? halfStroke * signX : 0); + var borderRight = right + (borderSkipped !== 'right' ? -halfStroke * signX : 0); + var borderTop = top + (borderSkipped !== 'top' ? halfStroke * signY : 0); + var borderBottom = bottom + (borderSkipped !== 'bottom' ? -halfStroke * signY : 0); + // not become a vertical line? + if (borderLeft !== borderRight) { + top = borderTop; + bottom = borderBottom; + } + // not become a horizontal line? + if (borderTop !== borderBottom) { + left = borderLeft; + right = borderRight; + } + } + + ctx.beginPath(); + ctx.fillStyle = vm.backgroundColor; + ctx.strokeStyle = vm.borderColor; + ctx.lineWidth = borderWidth; + + // Corner points, from bottom-left to bottom-right clockwise + // | 1 2 | + // | 0 3 | + var corners = [ + [left, bottom], + [left, top], + [right, top], + [right, bottom] + ]; + + // Find first (starting) corner with fallback to 'bottom' + var borders = ['bottom', 'left', 'top', 'right']; + var startCorner = borders.indexOf(borderSkipped, 0); + if (startCorner === -1) { + startCorner = 0; + } + + function cornerAt(index) { + return corners[(startCorner + index) % 4]; + } + + // Draw rectangle from 'startCorner' + var corner = cornerAt(0); + ctx.moveTo(corner[0], corner[1]); + + for (var i = 1; i < 4; i++) { + corner = cornerAt(i); + ctx.lineTo(corner[0], corner[1]); + } + + ctx.fill(); + if (borderWidth) { + ctx.stroke(); + } + }, + + height: function() { + var vm = this._view; + return vm.base - vm.y; + }, + + inRange: function(mouseX, mouseY) { + var inRange = false; + + if (this._view) { + var bounds = getBarBounds(this); + inRange = mouseX >= bounds.left && mouseX <= bounds.right && mouseY >= bounds.top && mouseY <= bounds.bottom; + } + + return inRange; + }, + + inLabelRange: function(mouseX, mouseY) { + var me = this; + if (!me._view) { + return false; + } + + var inRange = false; + var bounds = getBarBounds(me); + + if (isVertical(me)) { + inRange = mouseX >= bounds.left && mouseX <= bounds.right; + } else { + inRange = mouseY >= bounds.top && mouseY <= bounds.bottom; + } + + return inRange; + }, + + inXRange: function(mouseX) { + var bounds = getBarBounds(this); + return mouseX >= bounds.left && mouseX <= bounds.right; + }, + + inYRange: function(mouseY) { + var bounds = getBarBounds(this); + return mouseY >= bounds.top && mouseY <= bounds.bottom; + }, + + getCenterPoint: function() { + var vm = this._view; + var x, y; + if (isVertical(this)) { + x = vm.x; + y = (vm.y + vm.base) / 2; + } else { + x = (vm.x + vm.base) / 2; + y = vm.y; + } + + return {x: x, y: y}; + }, + + getArea: function() { + var vm = this._view; + return vm.width * Math.abs(vm.y - vm.base); + }, + + tooltipPosition: function() { + var vm = this._view; + return { + x: vm.x, + y: vm.y + }; + } +}); + +},{"25":25,"26":26}],40:[function(require,module,exports){ +'use strict'; + +module.exports = {}; +module.exports.Arc = require(36); +module.exports.Line = require(37); +module.exports.Point = require(38); +module.exports.Rectangle = require(39); + +},{"36":36,"37":37,"38":38,"39":39}],41:[function(require,module,exports){ +'use strict'; + +var helpers = require(42); + +/** + * @namespace Chart.helpers.canvas + */ +var exports = module.exports = { + /** + * Clears the entire canvas associated to the given `chart`. + * @param {Chart} chart - The chart for which to clear the canvas. + */ + clear: function(chart) { + chart.ctx.clearRect(0, 0, chart.width, chart.height); + }, + + /** + * Creates a "path" for a rectangle with rounded corners at position (x, y) with a + * given size (width, height) and the same `radius` for all corners. + * @param {CanvasRenderingContext2D} ctx - The canvas 2D Context. + * @param {Number} x - The x axis of the coordinate for the rectangle starting point. + * @param {Number} y - The y axis of the coordinate for the rectangle starting point. + * @param {Number} width - The rectangle's width. + * @param {Number} height - The rectangle's height. + * @param {Number} radius - The rounded amount (in pixels) for the four corners. + * @todo handle `radius` as top-left, top-right, bottom-right, bottom-left array/object? + */ + roundedRect: function(ctx, x, y, width, height, radius) { + if (radius) { + var rx = Math.min(radius, width / 2); + var ry = Math.min(radius, height / 2); + + ctx.moveTo(x + rx, y); + ctx.lineTo(x + width - rx, y); + ctx.quadraticCurveTo(x + width, y, x + width, y + ry); + ctx.lineTo(x + width, y + height - ry); + ctx.quadraticCurveTo(x + width, y + height, x + width - rx, y + height); + ctx.lineTo(x + rx, y + height); + ctx.quadraticCurveTo(x, y + height, x, y + height - ry); + ctx.lineTo(x, y + ry); + ctx.quadraticCurveTo(x, y, x + rx, y); + } else { + ctx.rect(x, y, width, height); + } + }, + + drawPoint: function(ctx, style, radius, x, y) { + var type, edgeLength, xOffset, yOffset, height, size; + + if (style && typeof style === 'object') { + type = style.toString(); + if (type === '[object HTMLImageElement]' || type === '[object HTMLCanvasElement]') { + ctx.drawImage(style, x - style.width / 2, y - style.height / 2, style.width, style.height); + return; + } + } + + if (isNaN(radius) || radius <= 0) { + return; + } + + switch (style) { + // Default includes circle + default: + ctx.beginPath(); + ctx.arc(x, y, radius, 0, Math.PI * 2); + ctx.closePath(); + ctx.fill(); + break; + case 'triangle': + ctx.beginPath(); + edgeLength = 3 * radius / Math.sqrt(3); + height = edgeLength * Math.sqrt(3) / 2; + ctx.moveTo(x - edgeLength / 2, y + height / 3); + ctx.lineTo(x + edgeLength / 2, y + height / 3); + ctx.lineTo(x, y - 2 * height / 3); + ctx.closePath(); + ctx.fill(); + break; + case 'rect': + size = 1 / Math.SQRT2 * radius; + ctx.beginPath(); + ctx.fillRect(x - size, y - size, 2 * size, 2 * size); + ctx.strokeRect(x - size, y - size, 2 * size, 2 * size); + break; + case 'rectRounded': + var offset = radius / Math.SQRT2; + var leftX = x - offset; + var topY = y - offset; + var sideSize = Math.SQRT2 * radius; + ctx.beginPath(); + this.roundedRect(ctx, leftX, topY, sideSize, sideSize, radius / 2); + ctx.closePath(); + ctx.fill(); + break; + case 'rectRot': + size = 1 / Math.SQRT2 * radius; + ctx.beginPath(); + ctx.moveTo(x - size, y); + ctx.lineTo(x, y + size); + ctx.lineTo(x + size, y); + ctx.lineTo(x, y - size); + ctx.closePath(); + ctx.fill(); + break; + case 'cross': + ctx.beginPath(); + ctx.moveTo(x, y + radius); + ctx.lineTo(x, y - radius); + ctx.moveTo(x - radius, y); + ctx.lineTo(x + radius, y); + ctx.closePath(); + break; + case 'crossRot': + ctx.beginPath(); + xOffset = Math.cos(Math.PI / 4) * radius; + yOffset = Math.sin(Math.PI / 4) * radius; + ctx.moveTo(x - xOffset, y - yOffset); + ctx.lineTo(x + xOffset, y + yOffset); + ctx.moveTo(x - xOffset, y + yOffset); + ctx.lineTo(x + xOffset, y - yOffset); + ctx.closePath(); + break; + case 'star': + ctx.beginPath(); + ctx.moveTo(x, y + radius); + ctx.lineTo(x, y - radius); + ctx.moveTo(x - radius, y); + ctx.lineTo(x + radius, y); + xOffset = Math.cos(Math.PI / 4) * radius; + yOffset = Math.sin(Math.PI / 4) * radius; + ctx.moveTo(x - xOffset, y - yOffset); + ctx.lineTo(x + xOffset, y + yOffset); + ctx.moveTo(x - xOffset, y + yOffset); + ctx.lineTo(x + xOffset, y - yOffset); + ctx.closePath(); + break; + case 'line': + ctx.beginPath(); + ctx.moveTo(x - radius, y); + ctx.lineTo(x + radius, y); + ctx.closePath(); + break; + case 'dash': + ctx.beginPath(); + ctx.moveTo(x, y); + ctx.lineTo(x + radius, y); + ctx.closePath(); + break; + } + + ctx.stroke(); + }, + + clipArea: function(ctx, area) { + ctx.save(); + ctx.beginPath(); + ctx.rect(area.left, area.top, area.right - area.left, area.bottom - area.top); + ctx.clip(); + }, + + unclipArea: function(ctx) { + ctx.restore(); + }, + + lineTo: function(ctx, previous, target, flip) { + if (target.steppedLine) { + if ((target.steppedLine === 'after' && !flip) || (target.steppedLine !== 'after' && flip)) { + ctx.lineTo(previous.x, target.y); + } else { + ctx.lineTo(target.x, previous.y); + } + ctx.lineTo(target.x, target.y); + return; + } + + if (!target.tension) { + ctx.lineTo(target.x, target.y); + return; + } + + ctx.bezierCurveTo( + flip ? previous.controlPointPreviousX : previous.controlPointNextX, + flip ? previous.controlPointPreviousY : previous.controlPointNextY, + flip ? target.controlPointNextX : target.controlPointPreviousX, + flip ? target.controlPointNextY : target.controlPointPreviousY, + target.x, + target.y); + } +}; + +// DEPRECATIONS + +/** + * Provided for backward compatibility, use Chart.helpers.canvas.clear instead. + * @namespace Chart.helpers.clear + * @deprecated since version 2.7.0 + * @todo remove at version 3 + * @private + */ +helpers.clear = exports.clear; + +/** + * Provided for backward compatibility, use Chart.helpers.canvas.roundedRect instead. + * @namespace Chart.helpers.drawRoundedRectangle + * @deprecated since version 2.7.0 + * @todo remove at version 3 + * @private + */ +helpers.drawRoundedRectangle = function(ctx) { + ctx.beginPath(); + exports.roundedRect.apply(exports, arguments); + ctx.closePath(); +}; + +},{"42":42}],42:[function(require,module,exports){ +'use strict'; + +/** + * @namespace Chart.helpers + */ +var helpers = { + /** + * An empty function that can be used, for example, for optional callback. + */ + noop: function() {}, + + /** + * Returns a unique id, sequentially generated from a global variable. + * @returns {Number} + * @function + */ + uid: (function() { + var id = 0; + return function() { + return id++; + }; + }()), + + /** + * Returns true if `value` is neither null nor undefined, else returns false. + * @param {*} value - The value to test. + * @returns {Boolean} + * @since 2.7.0 + */ + isNullOrUndef: function(value) { + return value === null || typeof value === 'undefined'; + }, + + /** + * Returns true if `value` is an array, else returns false. + * @param {*} value - The value to test. + * @returns {Boolean} + * @function + */ + isArray: Array.isArray ? Array.isArray : function(value) { + return Object.prototype.toString.call(value) === '[object Array]'; + }, + + /** + * Returns true if `value` is an object (excluding null), else returns false. + * @param {*} value - The value to test. + * @returns {Boolean} + * @since 2.7.0 + */ + isObject: function(value) { + return value !== null && Object.prototype.toString.call(value) === '[object Object]'; + }, + + /** + * Returns `value` if defined, else returns `defaultValue`. + * @param {*} value - The value to return if defined. + * @param {*} defaultValue - The value to return if `value` is undefined. + * @returns {*} + */ + valueOrDefault: function(value, defaultValue) { + return typeof value === 'undefined' ? defaultValue : value; + }, + + /** + * Returns value at the given `index` in array if defined, else returns `defaultValue`. + * @param {Array} value - The array to lookup for value at `index`. + * @param {Number} index - The index in `value` to lookup for value. + * @param {*} defaultValue - The value to return if `value[index]` is undefined. + * @returns {*} + */ + valueAtIndexOrDefault: function(value, index, defaultValue) { + return helpers.valueOrDefault(helpers.isArray(value) ? value[index] : value, defaultValue); + }, + + /** + * Calls `fn` with the given `args` in the scope defined by `thisArg` and returns the + * value returned by `fn`. If `fn` is not a function, this method returns undefined. + * @param {Function} fn - The function to call. + * @param {Array|undefined|null} args - The arguments with which `fn` should be called. + * @param {Object} [thisArg] - The value of `this` provided for the call to `fn`. + * @returns {*} + */ + callback: function(fn, args, thisArg) { + if (fn && typeof fn.call === 'function') { + return fn.apply(thisArg, args); + } + }, + + /** + * Note(SB) for performance sake, this method should only be used when loopable type + * is unknown or in none intensive code (not called often and small loopable). Else + * it's preferable to use a regular for() loop and save extra function calls. + * @param {Object|Array} loopable - The object or array to be iterated. + * @param {Function} fn - The function to call for each item. + * @param {Object} [thisArg] - The value of `this` provided for the call to `fn`. + * @param {Boolean} [reverse] - If true, iterates backward on the loopable. + */ + each: function(loopable, fn, thisArg, reverse) { + var i, len, keys; + if (helpers.isArray(loopable)) { + len = loopable.length; + if (reverse) { + for (i = len - 1; i >= 0; i--) { + fn.call(thisArg, loopable[i], i); + } + } else { + for (i = 0; i < len; i++) { + fn.call(thisArg, loopable[i], i); + } + } + } else if (helpers.isObject(loopable)) { + keys = Object.keys(loopable); + len = keys.length; + for (i = 0; i < len; i++) { + fn.call(thisArg, loopable[keys[i]], keys[i]); + } + } + }, + + /** + * Returns true if the `a0` and `a1` arrays have the same content, else returns false. + * @see http://stackoverflow.com/a/14853974 + * @param {Array} a0 - The array to compare + * @param {Array} a1 - The array to compare + * @returns {Boolean} + */ + arrayEquals: function(a0, a1) { + var i, ilen, v0, v1; + + if (!a0 || !a1 || a0.length !== a1.length) { + return false; + } + + for (i = 0, ilen = a0.length; i < ilen; ++i) { + v0 = a0[i]; + v1 = a1[i]; + + if (v0 instanceof Array && v1 instanceof Array) { + if (!helpers.arrayEquals(v0, v1)) { + return false; + } + } else if (v0 !== v1) { + // NOTE: two different object instances will never be equal: {x:20} != {x:20} + return false; + } + } + + return true; + }, + + /** + * Returns a deep copy of `source` without keeping references on objects and arrays. + * @param {*} source - The value to clone. + * @returns {*} + */ + clone: function(source) { + if (helpers.isArray(source)) { + return source.map(helpers.clone); + } + + if (helpers.isObject(source)) { + var target = {}; + var keys = Object.keys(source); + var klen = keys.length; + var k = 0; + + for (; k < klen; ++k) { + target[keys[k]] = helpers.clone(source[keys[k]]); + } + + return target; + } + + return source; + }, + + /** + * The default merger when Chart.helpers.merge is called without merger option. + * Note(SB): this method is also used by configMerge and scaleMerge as fallback. + * @private + */ + _merger: function(key, target, source, options) { + var tval = target[key]; + var sval = source[key]; + + if (helpers.isObject(tval) && helpers.isObject(sval)) { + helpers.merge(tval, sval, options); + } else { + target[key] = helpers.clone(sval); + } + }, + + /** + * Merges source[key] in target[key] only if target[key] is undefined. + * @private + */ + _mergerIf: function(key, target, source) { + var tval = target[key]; + var sval = source[key]; + + if (helpers.isObject(tval) && helpers.isObject(sval)) { + helpers.mergeIf(tval, sval); + } else if (!target.hasOwnProperty(key)) { + target[key] = helpers.clone(sval); + } + }, + + /** + * Recursively deep copies `source` properties into `target` with the given `options`. + * IMPORTANT: `target` is not cloned and will be updated with `source` properties. + * @param {Object} target - The target object in which all sources are merged into. + * @param {Object|Array(Object)} source - Object(s) to merge into `target`. + * @param {Object} [options] - Merging options: + * @param {Function} [options.merger] - The merge method (key, target, source, options) + * @returns {Object} The `target` object. + */ + merge: function(target, source, options) { + var sources = helpers.isArray(source) ? source : [source]; + var ilen = sources.length; + var merge, i, keys, klen, k; + + if (!helpers.isObject(target)) { + return target; + } + + options = options || {}; + merge = options.merger || helpers._merger; + + for (i = 0; i < ilen; ++i) { + source = sources[i]; + if (!helpers.isObject(source)) { + continue; + } + + keys = Object.keys(source); + for (k = 0, klen = keys.length; k < klen; ++k) { + merge(keys[k], target, source, options); + } + } + + return target; + }, + + /** + * Recursively deep copies `source` properties into `target` *only* if not defined in target. + * IMPORTANT: `target` is not cloned and will be updated with `source` properties. + * @param {Object} target - The target object in which all sources are merged into. + * @param {Object|Array(Object)} source - Object(s) to merge into `target`. + * @returns {Object} The `target` object. + */ + mergeIf: function(target, source) { + return helpers.merge(target, source, {merger: helpers._mergerIf}); + }, + + /** + * Applies the contents of two or more objects together into the first object. + * @param {Object} target - The target object in which all objects are merged into. + * @param {Object} arg1 - Object containing additional properties to merge in target. + * @param {Object} argN - Additional objects containing properties to merge in target. + * @returns {Object} The `target` object. + */ + extend: function(target) { + var setFn = function(value, key) { + target[key] = value; + }; + for (var i = 1, ilen = arguments.length; i < ilen; ++i) { + helpers.each(arguments[i], setFn); + } + return target; + }, + + /** + * Basic javascript inheritance based on the model created in Backbone.js + */ + inherits: function(extensions) { + var me = this; + var ChartElement = (extensions && extensions.hasOwnProperty('constructor')) ? extensions.constructor : function() { + return me.apply(this, arguments); + }; + + var Surrogate = function() { + this.constructor = ChartElement; + }; + + Surrogate.prototype = me.prototype; + ChartElement.prototype = new Surrogate(); + ChartElement.extend = helpers.inherits; + + if (extensions) { + helpers.extend(ChartElement.prototype, extensions); + } + + ChartElement.__super__ = me.prototype; + return ChartElement; + } +}; + +module.exports = helpers; + +// DEPRECATIONS + +/** + * Provided for backward compatibility, use Chart.helpers.callback instead. + * @function Chart.helpers.callCallback + * @deprecated since version 2.6.0 + * @todo remove at version 3 + * @private + */ +helpers.callCallback = helpers.callback; + +/** + * Provided for backward compatibility, use Array.prototype.indexOf instead. + * Array.prototype.indexOf compatibility: Chrome, Opera, Safari, FF1.5+, IE9+ + * @function Chart.helpers.indexOf + * @deprecated since version 2.7.0 + * @todo remove at version 3 + * @private + */ +helpers.indexOf = function(array, item, fromIndex) { + return Array.prototype.indexOf.call(array, item, fromIndex); +}; + +/** + * Provided for backward compatibility, use Chart.helpers.valueOrDefault instead. + * @function Chart.helpers.getValueOrDefault + * @deprecated since version 2.7.0 + * @todo remove at version 3 + * @private + */ +helpers.getValueOrDefault = helpers.valueOrDefault; + +/** + * Provided for backward compatibility, use Chart.helpers.valueAtIndexOrDefault instead. + * @function Chart.helpers.getValueAtIndexOrDefault + * @deprecated since version 2.7.0 + * @todo remove at version 3 + * @private + */ +helpers.getValueAtIndexOrDefault = helpers.valueAtIndexOrDefault; + +},{}],43:[function(require,module,exports){ +'use strict'; + +var helpers = require(42); + +/** + * Easing functions adapted from Robert Penner's easing equations. + * @namespace Chart.helpers.easingEffects + * @see http://www.robertpenner.com/easing/ + */ +var effects = { + linear: function(t) { + return t; + }, + + easeInQuad: function(t) { + return t * t; + }, + + easeOutQuad: function(t) { + return -t * (t - 2); + }, + + easeInOutQuad: function(t) { + if ((t /= 0.5) < 1) { + return 0.5 * t * t; + } + return -0.5 * ((--t) * (t - 2) - 1); + }, + + easeInCubic: function(t) { + return t * t * t; + }, + + easeOutCubic: function(t) { + return (t = t - 1) * t * t + 1; + }, + + easeInOutCubic: function(t) { + if ((t /= 0.5) < 1) { + return 0.5 * t * t * t; + } + return 0.5 * ((t -= 2) * t * t + 2); + }, + + easeInQuart: function(t) { + return t * t * t * t; + }, + + easeOutQuart: function(t) { + return -((t = t - 1) * t * t * t - 1); + }, + + easeInOutQuart: function(t) { + if ((t /= 0.5) < 1) { + return 0.5 * t * t * t * t; + } + return -0.5 * ((t -= 2) * t * t * t - 2); + }, + + easeInQuint: function(t) { + return t * t * t * t * t; + }, + + easeOutQuint: function(t) { + return (t = t - 1) * t * t * t * t + 1; + }, + + easeInOutQuint: function(t) { + if ((t /= 0.5) < 1) { + return 0.5 * t * t * t * t * t; + } + return 0.5 * ((t -= 2) * t * t * t * t + 2); + }, + + easeInSine: function(t) { + return -Math.cos(t * (Math.PI / 2)) + 1; + }, + + easeOutSine: function(t) { + return Math.sin(t * (Math.PI / 2)); + }, + + easeInOutSine: function(t) { + return -0.5 * (Math.cos(Math.PI * t) - 1); + }, + + easeInExpo: function(t) { + return (t === 0) ? 0 : Math.pow(2, 10 * (t - 1)); + }, + + easeOutExpo: function(t) { + return (t === 1) ? 1 : -Math.pow(2, -10 * t) + 1; + }, + + easeInOutExpo: function(t) { + if (t === 0) { + return 0; + } + if (t === 1) { + return 1; + } + if ((t /= 0.5) < 1) { + return 0.5 * Math.pow(2, 10 * (t - 1)); + } + return 0.5 * (-Math.pow(2, -10 * --t) + 2); + }, + + easeInCirc: function(t) { + if (t >= 1) { + return t; + } + return -(Math.sqrt(1 - t * t) - 1); + }, + + easeOutCirc: function(t) { + return Math.sqrt(1 - (t = t - 1) * t); + }, + + easeInOutCirc: function(t) { + if ((t /= 0.5) < 1) { + return -0.5 * (Math.sqrt(1 - t * t) - 1); + } + return 0.5 * (Math.sqrt(1 - (t -= 2) * t) + 1); + }, + + easeInElastic: function(t) { + var s = 1.70158; + var p = 0; + var a = 1; + if (t === 0) { + return 0; + } + if (t === 1) { + return 1; + } + if (!p) { + p = 0.3; + } + if (a < 1) { + a = 1; + s = p / 4; + } else { + s = p / (2 * Math.PI) * Math.asin(1 / a); + } + return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t - s) * (2 * Math.PI) / p)); + }, + + easeOutElastic: function(t) { + var s = 1.70158; + var p = 0; + var a = 1; + if (t === 0) { + return 0; + } + if (t === 1) { + return 1; + } + if (!p) { + p = 0.3; + } + if (a < 1) { + a = 1; + s = p / 4; + } else { + s = p / (2 * Math.PI) * Math.asin(1 / a); + } + return a * Math.pow(2, -10 * t) * Math.sin((t - s) * (2 * Math.PI) / p) + 1; + }, + + easeInOutElastic: function(t) { + var s = 1.70158; + var p = 0; + var a = 1; + if (t === 0) { + return 0; + } + if ((t /= 0.5) === 2) { + return 1; + } + if (!p) { + p = 0.45; + } + if (a < 1) { + a = 1; + s = p / 4; + } else { + s = p / (2 * Math.PI) * Math.asin(1 / a); + } + if (t < 1) { + return -0.5 * (a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t - s) * (2 * Math.PI) / p)); + } + return a * Math.pow(2, -10 * (t -= 1)) * Math.sin((t - s) * (2 * Math.PI) / p) * 0.5 + 1; + }, + easeInBack: function(t) { + var s = 1.70158; + return t * t * ((s + 1) * t - s); + }, + + easeOutBack: function(t) { + var s = 1.70158; + return (t = t - 1) * t * ((s + 1) * t + s) + 1; + }, + + easeInOutBack: function(t) { + var s = 1.70158; + if ((t /= 0.5) < 1) { + return 0.5 * (t * t * (((s *= (1.525)) + 1) * t - s)); + } + return 0.5 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2); + }, + + easeInBounce: function(t) { + return 1 - effects.easeOutBounce(1 - t); + }, + + easeOutBounce: function(t) { + if (t < (1 / 2.75)) { + return 7.5625 * t * t; + } + if (t < (2 / 2.75)) { + return 7.5625 * (t -= (1.5 / 2.75)) * t + 0.75; + } + if (t < (2.5 / 2.75)) { + return 7.5625 * (t -= (2.25 / 2.75)) * t + 0.9375; + } + return 7.5625 * (t -= (2.625 / 2.75)) * t + 0.984375; + }, + + easeInOutBounce: function(t) { + if (t < 0.5) { + return effects.easeInBounce(t * 2) * 0.5; + } + return effects.easeOutBounce(t * 2 - 1) * 0.5 + 0.5; + } +}; + +module.exports = { + effects: effects +}; + +// DEPRECATIONS + +/** + * Provided for backward compatibility, use Chart.helpers.easing.effects instead. + * @function Chart.helpers.easingEffects + * @deprecated since version 2.7.0 + * @todo remove at version 3 + * @private + */ +helpers.easingEffects = effects; + +},{"42":42}],44:[function(require,module,exports){ +'use strict'; + +var helpers = require(42); + +/** + * @alias Chart.helpers.options + * @namespace + */ +module.exports = { + /** + * Converts the given line height `value` in pixels for a specific font `size`. + * @param {Number|String} value - The lineHeight to parse (eg. 1.6, '14px', '75%', '1.6em'). + * @param {Number} size - The font size (in pixels) used to resolve relative `value`. + * @returns {Number} The effective line height in pixels (size * 1.2 if value is invalid). + * @see https://developer.mozilla.org/en-US/docs/Web/CSS/line-height + * @since 2.7.0 + */ + toLineHeight: function(value, size) { + var matches = ('' + value).match(/^(normal|(\d+(?:\.\d+)?)(px|em|%)?)$/); + if (!matches || matches[1] === 'normal') { + return size * 1.2; + } + + value = +matches[2]; + + switch (matches[3]) { + case 'px': + return value; + case '%': + value /= 100; + break; + default: + break; + } + + return size * value; + }, + + /** + * Converts the given value into a padding object with pre-computed width/height. + * @param {Number|Object} value - If a number, set the value to all TRBL component, + * else, if and object, use defined properties and sets undefined ones to 0. + * @returns {Object} The padding values (top, right, bottom, left, width, height) + * @since 2.7.0 + */ + toPadding: function(value) { + var t, r, b, l; + + if (helpers.isObject(value)) { + t = +value.top || 0; + r = +value.right || 0; + b = +value.bottom || 0; + l = +value.left || 0; + } else { + t = r = b = l = +value || 0; + } + + return { + top: t, + right: r, + bottom: b, + left: l, + height: t + b, + width: l + r + }; + }, + + /** + * Evaluates the given `inputs` sequentially and returns the first defined value. + * @param {Array[]} inputs - An array of values, falling back to the last value. + * @param {Object} [context] - If defined and the current value is a function, the value + * is called with `context` as first argument and the result becomes the new input. + * @param {Number} [index] - If defined and the current value is an array, the value + * at `index` become the new input. + * @since 2.7.0 + */ + resolve: function(inputs, context, index) { + var i, ilen, value; + + for (i = 0, ilen = inputs.length; i < ilen; ++i) { + value = inputs[i]; + if (value === undefined) { + continue; + } + if (context !== undefined && typeof value === 'function') { + value = value(context); + } + if (index !== undefined && helpers.isArray(value)) { + value = value[index]; + } + if (value !== undefined) { + return value; + } + } + } +}; + +},{"42":42}],45:[function(require,module,exports){ +'use strict'; + +module.exports = require(42); +module.exports.easing = require(43); +module.exports.canvas = require(41); +module.exports.options = require(44); + +},{"41":41,"42":42,"43":43,"44":44}],46:[function(require,module,exports){ +/** + * Platform fallback implementation (minimal). + * @see https://github.com/chartjs/Chart.js/pull/4591#issuecomment-319575939 + */ + +module.exports = { + acquireContext: function(item) { + if (item && item.canvas) { + // Support for any object associated to a canvas (including a context2d) + item = item.canvas; + } + + return item && item.getContext('2d') || null; + } +}; + +},{}],47:[function(require,module,exports){ +/** + * Chart.Platform implementation for targeting a web browser + */ + +'use strict'; + +var helpers = require(45); + +var EXPANDO_KEY = '$chartjs'; +var CSS_PREFIX = 'chartjs-'; +var CSS_RENDER_MONITOR = CSS_PREFIX + 'render-monitor'; +var CSS_RENDER_ANIMATION = CSS_PREFIX + 'render-animation'; +var ANIMATION_START_EVENTS = ['animationstart', 'webkitAnimationStart']; + +/** + * DOM event types -> Chart.js event types. + * Note: only events with different types are mapped. + * @see https://developer.mozilla.org/en-US/docs/Web/Events + */ +var EVENT_TYPES = { + touchstart: 'mousedown', + touchmove: 'mousemove', + touchend: 'mouseup', + pointerenter: 'mouseenter', + pointerdown: 'mousedown', + pointermove: 'mousemove', + pointerup: 'mouseup', + pointerleave: 'mouseout', + pointerout: 'mouseout' +}; + +/** + * The "used" size is the final value of a dimension property after all calculations have + * been performed. This method uses the computed style of `element` but returns undefined + * if the computed style is not expressed in pixels. That can happen in some cases where + * `element` has a size relative to its parent and this last one is not yet displayed, + * for example because of `display: none` on a parent node. + * @see https://developer.mozilla.org/en-US/docs/Web/CSS/used_value + * @returns {Number} Size in pixels or undefined if unknown. + */ +function readUsedSize(element, property) { + var value = helpers.getStyle(element, property); + var matches = value && value.match(/^(\d+)(\.\d+)?px$/); + return matches ? Number(matches[1]) : undefined; +} + +/** + * Initializes the canvas style and render size without modifying the canvas display size, + * since responsiveness is handled by the controller.resize() method. The config is used + * to determine the aspect ratio to apply in case no explicit height has been specified. + */ +function initCanvas(canvas, config) { + var style = canvas.style; + + // NOTE(SB) canvas.getAttribute('width') !== canvas.width: in the first case it + // returns null or '' if no explicit value has been set to the canvas attribute. + var renderHeight = canvas.getAttribute('height'); + var renderWidth = canvas.getAttribute('width'); + + // Chart.js modifies some canvas values that we want to restore on destroy + canvas[EXPANDO_KEY] = { + initial: { + height: renderHeight, + width: renderWidth, + style: { + display: style.display, + height: style.height, + width: style.width + } + } + }; + + // Force canvas to display as block to avoid extra space caused by inline + // elements, which would interfere with the responsive resize process. + // https://github.com/chartjs/Chart.js/issues/2538 + style.display = style.display || 'block'; + + if (renderWidth === null || renderWidth === '') { + var displayWidth = readUsedSize(canvas, 'width'); + if (displayWidth !== undefined) { + canvas.width = displayWidth; + } + } + + if (renderHeight === null || renderHeight === '') { + if (canvas.style.height === '') { + // If no explicit render height and style height, let's apply the aspect ratio, + // which one can be specified by the user but also by charts as default option + // (i.e. options.aspectRatio). If not specified, use canvas aspect ratio of 2. + canvas.height = canvas.width / (config.options.aspectRatio || 2); + } else { + var displayHeight = readUsedSize(canvas, 'height'); + if (displayWidth !== undefined) { + canvas.height = displayHeight; + } + } + } + + return canvas; +} + +/** + * Detects support for options object argument in addEventListener. + * https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#Safely_detecting_option_support + * @private + */ +var supportsEventListenerOptions = (function() { + var supports = false; + try { + var options = Object.defineProperty({}, 'passive', { + get: function() { + supports = true; + } + }); + window.addEventListener('e', null, options); + } catch (e) { + // continue regardless of error + } + return supports; +}()); + +// Default passive to true as expected by Chrome for 'touchstart' and 'touchend' events. +// https://github.com/chartjs/Chart.js/issues/4287 +var eventListenerOptions = supportsEventListenerOptions ? {passive: true} : false; + +function addEventListener(node, type, listener) { + node.addEventListener(type, listener, eventListenerOptions); +} + +function removeEventListener(node, type, listener) { + node.removeEventListener(type, listener, eventListenerOptions); +} + +function createEvent(type, chart, x, y, nativeEvent) { + return { + type: type, + chart: chart, + native: nativeEvent || null, + x: x !== undefined ? x : null, + y: y !== undefined ? y : null, + }; +} + +function fromNativeEvent(event, chart) { + var type = EVENT_TYPES[event.type] || event.type; + var pos = helpers.getRelativePosition(event, chart); + return createEvent(type, chart, pos.x, pos.y, event); +} + +function throttled(fn, thisArg) { + var ticking = false; + var args = []; + + return function() { + args = Array.prototype.slice.call(arguments); + thisArg = thisArg || this; + + if (!ticking) { + ticking = true; + helpers.requestAnimFrame.call(window, function() { + ticking = false; + fn.apply(thisArg, args); + }); + } + }; +} + +// Implementation based on https://github.com/marcj/css-element-queries +function createResizer(handler) { + var resizer = document.createElement('div'); + var cls = CSS_PREFIX + 'size-monitor'; + var maxSize = 1000000; + var style = + 'position:absolute;' + + 'left:0;' + + 'top:0;' + + 'right:0;' + + 'bottom:0;' + + 'overflow:hidden;' + + 'pointer-events:none;' + + 'visibility:hidden;' + + 'z-index:-1;'; + + resizer.style.cssText = style; + resizer.className = cls; + resizer.innerHTML = + '
' + + '
' + + '
' + + '
' + + '
' + + '
' + + '
' + + '
'; + + var expand = resizer.childNodes[0]; + var shrink = resizer.childNodes[1]; + + resizer._reset = function() { + expand.scrollLeft = maxSize; + expand.scrollTop = maxSize; + shrink.scrollLeft = maxSize; + shrink.scrollTop = maxSize; + }; + var onScroll = function() { + resizer._reset(); + handler(); + }; + + addEventListener(expand, 'scroll', onScroll.bind(expand, 'expand')); + addEventListener(shrink, 'scroll', onScroll.bind(shrink, 'shrink')); + + return resizer; +} + +// https://davidwalsh.name/detect-node-insertion +function watchForRender(node, handler) { + var expando = node[EXPANDO_KEY] || (node[EXPANDO_KEY] = {}); + var proxy = expando.renderProxy = function(e) { + if (e.animationName === CSS_RENDER_ANIMATION) { + handler(); + } + }; + + helpers.each(ANIMATION_START_EVENTS, function(type) { + addEventListener(node, type, proxy); + }); + + // #4737: Chrome might skip the CSS animation when the CSS_RENDER_MONITOR class + // is removed then added back immediately (same animation frame?). Accessing the + // `offsetParent` property will force a reflow and re-evaluate the CSS animation. + // https://gist.github.com/paulirish/5d52fb081b3570c81e3a#box-metrics + // https://github.com/chartjs/Chart.js/issues/4737 + expando.reflow = !!node.offsetParent; + + node.classList.add(CSS_RENDER_MONITOR); +} + +function unwatchForRender(node) { + var expando = node[EXPANDO_KEY] || {}; + var proxy = expando.renderProxy; + + if (proxy) { + helpers.each(ANIMATION_START_EVENTS, function(type) { + removeEventListener(node, type, proxy); + }); + + delete expando.renderProxy; + } + + node.classList.remove(CSS_RENDER_MONITOR); +} + +function addResizeListener(node, listener, chart) { + var expando = node[EXPANDO_KEY] || (node[EXPANDO_KEY] = {}); + + // Let's keep track of this added resizer and thus avoid DOM query when removing it. + var resizer = expando.resizer = createResizer(throttled(function() { + if (expando.resizer) { + return listener(createEvent('resize', chart)); + } + })); + + // The resizer needs to be attached to the node parent, so we first need to be + // sure that `node` is attached to the DOM before injecting the resizer element. + watchForRender(node, function() { + if (expando.resizer) { + var container = node.parentNode; + if (container && container !== resizer.parentNode) { + container.insertBefore(resizer, container.firstChild); + } + + // The container size might have changed, let's reset the resizer state. + resizer._reset(); + } + }); +} + +function removeResizeListener(node) { + var expando = node[EXPANDO_KEY] || {}; + var resizer = expando.resizer; + + delete expando.resizer; + unwatchForRender(node); + + if (resizer && resizer.parentNode) { + resizer.parentNode.removeChild(resizer); + } +} + +function injectCSS(platform, css) { + // http://stackoverflow.com/q/3922139 + var style = platform._style || document.createElement('style'); + if (!platform._style) { + platform._style = style; + css = '/* Chart.js */\n' + css; + style.setAttribute('type', 'text/css'); + document.getElementsByTagName('head')[0].appendChild(style); + } + + style.appendChild(document.createTextNode(css)); +} + +module.exports = { + /** + * This property holds whether this platform is enabled for the current environment. + * Currently used by platform.js to select the proper implementation. + * @private + */ + _enabled: typeof window !== 'undefined' && typeof document !== 'undefined', + + initialize: function() { + var keyframes = 'from{opacity:0.99}to{opacity:1}'; + + injectCSS(this, + // DOM rendering detection + // https://davidwalsh.name/detect-node-insertion + '@-webkit-keyframes ' + CSS_RENDER_ANIMATION + '{' + keyframes + '}' + + '@keyframes ' + CSS_RENDER_ANIMATION + '{' + keyframes + '}' + + '.' + CSS_RENDER_MONITOR + '{' + + '-webkit-animation:' + CSS_RENDER_ANIMATION + ' 0.001s;' + + 'animation:' + CSS_RENDER_ANIMATION + ' 0.001s;' + + '}' + ); + }, + + acquireContext: function(item, config) { + if (typeof item === 'string') { + item = document.getElementById(item); + } else if (item.length) { + // Support for array based queries (such as jQuery) + item = item[0]; + } + + if (item && item.canvas) { + // Support for any object associated to a canvas (including a context2d) + item = item.canvas; + } + + // To prevent canvas fingerprinting, some add-ons undefine the getContext + // method, for example: https://github.com/kkapsner/CanvasBlocker + // https://github.com/chartjs/Chart.js/issues/2807 + var context = item && item.getContext && item.getContext('2d'); + + // `instanceof HTMLCanvasElement/CanvasRenderingContext2D` fails when the item is + // inside an iframe or when running in a protected environment. We could guess the + // types from their toString() value but let's keep things flexible and assume it's + // a sufficient condition if the item has a context2D which has item as `canvas`. + // https://github.com/chartjs/Chart.js/issues/3887 + // https://github.com/chartjs/Chart.js/issues/4102 + // https://github.com/chartjs/Chart.js/issues/4152 + if (context && context.canvas === item) { + initCanvas(item, config); + return context; + } + + return null; + }, + + releaseContext: function(context) { + var canvas = context.canvas; + if (!canvas[EXPANDO_KEY]) { + return; + } + + var initial = canvas[EXPANDO_KEY].initial; + ['height', 'width'].forEach(function(prop) { + var value = initial[prop]; + if (helpers.isNullOrUndef(value)) { + canvas.removeAttribute(prop); + } else { + canvas.setAttribute(prop, value); + } + }); + + helpers.each(initial.style || {}, function(value, key) { + canvas.style[key] = value; + }); + + // The canvas render size might have been changed (and thus the state stack discarded), + // we can't use save() and restore() to restore the initial state. So make sure that at + // least the canvas context is reset to the default state by setting the canvas width. + // https://www.w3.org/TR/2011/WD-html5-20110525/the-canvas-element.html + canvas.width = canvas.width; + + delete canvas[EXPANDO_KEY]; + }, + + addEventListener: function(chart, type, listener) { + var canvas = chart.canvas; + if (type === 'resize') { + // Note: the resize event is not supported on all browsers. + addResizeListener(canvas, listener, chart); + return; + } + + var expando = listener[EXPANDO_KEY] || (listener[EXPANDO_KEY] = {}); + var proxies = expando.proxies || (expando.proxies = {}); + var proxy = proxies[chart.id + '_' + type] = function(event) { + listener(fromNativeEvent(event, chart)); + }; + + addEventListener(canvas, type, proxy); + }, + + removeEventListener: function(chart, type, listener) { + var canvas = chart.canvas; + if (type === 'resize') { + // Note: the resize event is not supported on all browsers. + removeResizeListener(canvas, listener); + return; + } + + var expando = listener[EXPANDO_KEY] || {}; + var proxies = expando.proxies || {}; + var proxy = proxies[chart.id + '_' + type]; + if (!proxy) { + return; + } + + removeEventListener(canvas, type, proxy); + } +}; + +// DEPRECATIONS + +/** + * Provided for backward compatibility, use EventTarget.addEventListener instead. + * EventTarget.addEventListener compatibility: Chrome, Opera 7, Safari, FF1.5+, IE9+ + * @see https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener + * @function Chart.helpers.addEvent + * @deprecated since version 2.7.0 + * @todo remove at version 3 + * @private + */ +helpers.addEvent = addEventListener; + +/** + * Provided for backward compatibility, use EventTarget.removeEventListener instead. + * EventTarget.removeEventListener compatibility: Chrome, Opera 7, Safari, FF1.5+, IE9+ + * @see https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/removeEventListener + * @function Chart.helpers.removeEvent + * @deprecated since version 2.7.0 + * @todo remove at version 3 + * @private + */ +helpers.removeEvent = removeEventListener; + +},{"45":45}],48:[function(require,module,exports){ +'use strict'; + +var helpers = require(45); +var basic = require(46); +var dom = require(47); + +// @TODO Make possible to select another platform at build time. +var implementation = dom._enabled ? dom : basic; + +/** + * @namespace Chart.platform + * @see https://chartjs.gitbooks.io/proposals/content/Platform.html + * @since 2.4.0 + */ +module.exports = helpers.extend({ + /** + * @since 2.7.0 + */ + initialize: function() {}, + + /** + * Called at chart construction time, returns a context2d instance implementing + * the [W3C Canvas 2D Context API standard]{@link https://www.w3.org/TR/2dcontext/}. + * @param {*} item - The native item from which to acquire context (platform specific) + * @param {Object} options - The chart options + * @returns {CanvasRenderingContext2D} context2d instance + */ + acquireContext: function() {}, + + /** + * Called at chart destruction time, releases any resources associated to the context + * previously returned by the acquireContext() method. + * @param {CanvasRenderingContext2D} context - The context2d instance + * @returns {Boolean} true if the method succeeded, else false + */ + releaseContext: function() {}, + + /** + * Registers the specified listener on the given chart. + * @param {Chart} chart - Chart from which to listen for event + * @param {String} type - The ({@link IEvent}) type to listen for + * @param {Function} listener - Receives a notification (an object that implements + * the {@link IEvent} interface) when an event of the specified type occurs. + */ + addEventListener: function() {}, + + /** + * Removes the specified listener previously registered with addEventListener. + * @param {Chart} chart -Chart from which to remove the listener + * @param {String} type - The ({@link IEvent}) type to remove + * @param {Function} listener - The listener function to remove from the event target. + */ + removeEventListener: function() {} + +}, implementation); + +/** + * @interface IPlatform + * Allows abstracting platform dependencies away from the chart + * @borrows Chart.platform.acquireContext as acquireContext + * @borrows Chart.platform.releaseContext as releaseContext + * @borrows Chart.platform.addEventListener as addEventListener + * @borrows Chart.platform.removeEventListener as removeEventListener + */ + +/** + * @interface IEvent + * @prop {String} type - The event type name, possible values are: + * 'contextmenu', 'mouseenter', 'mousedown', 'mousemove', 'mouseup', 'mouseout', + * 'click', 'dblclick', 'keydown', 'keypress', 'keyup' and 'resize' + * @prop {*} native - The original native event (null for emulated events, e.g. 'resize') + * @prop {Number} x - The mouse x position, relative to the canvas (null for incompatible events) + * @prop {Number} y - The mouse y position, relative to the canvas (null for incompatible events) + */ + +},{"45":45,"46":46,"47":47}],49:[function(require,module,exports){ +/** + * Plugin based on discussion from the following Chart.js issues: + * @see https://github.com/chartjs/Chart.js/issues/2380#issuecomment-279961569 + * @see https://github.com/chartjs/Chart.js/issues/2440#issuecomment-256461897 + */ + +'use strict'; + +var defaults = require(25); +var elements = require(40); +var helpers = require(45); + +defaults._set('global', { + plugins: { + filler: { + propagate: true + } + } +}); + +module.exports = function() { + + var mappers = { + dataset: function(source) { + var index = source.fill; + var chart = source.chart; + var meta = chart.getDatasetMeta(index); + var visible = meta && chart.isDatasetVisible(index); + var points = (visible && meta.dataset._children) || []; + var length = points.length || 0; + + return !length ? null : function(point, i) { + return (i < length && points[i]._view) || null; + }; + }, + + boundary: function(source) { + var boundary = source.boundary; + var x = boundary ? boundary.x : null; + var y = boundary ? boundary.y : null; + + return function(point) { + return { + x: x === null ? point.x : x, + y: y === null ? point.y : y, + }; + }; + } + }; + + // @todo if (fill[0] === '#') + function decodeFill(el, index, count) { + var model = el._model || {}; + var fill = model.fill; + var target; + + if (fill === undefined) { + fill = !!model.backgroundColor; + } + + if (fill === false || fill === null) { + return false; + } + + if (fill === true) { + return 'origin'; + } + + target = parseFloat(fill, 10); + if (isFinite(target) && Math.floor(target) === target) { + if (fill[0] === '-' || fill[0] === '+') { + target = index + target; + } + + if (target === index || target < 0 || target >= count) { + return false; + } + + return target; + } + + switch (fill) { + // compatibility + case 'bottom': + return 'start'; + case 'top': + return 'end'; + case 'zero': + return 'origin'; + // supported boundaries + case 'origin': + case 'start': + case 'end': + return fill; + // invalid fill values + default: + return false; + } + } + + function computeBoundary(source) { + var model = source.el._model || {}; + var scale = source.el._scale || {}; + var fill = source.fill; + var target = null; + var horizontal; + + if (isFinite(fill)) { + return null; + } + + // Backward compatibility: until v3, we still need to support boundary values set on + // the model (scaleTop, scaleBottom and scaleZero) because some external plugins and + // controllers might still use it (e.g. the Smith chart). + + if (fill === 'start') { + target = model.scaleBottom === undefined ? scale.bottom : model.scaleBottom; + } else if (fill === 'end') { + target = model.scaleTop === undefined ? scale.top : model.scaleTop; + } else if (model.scaleZero !== undefined) { + target = model.scaleZero; + } else if (scale.getBasePosition) { + target = scale.getBasePosition(); + } else if (scale.getBasePixel) { + target = scale.getBasePixel(); + } + + if (target !== undefined && target !== null) { + if (target.x !== undefined && target.y !== undefined) { + return target; + } + + if (typeof target === 'number' && isFinite(target)) { + horizontal = scale.isHorizontal(); + return { + x: horizontal ? target : null, + y: horizontal ? null : target + }; + } + } + + return null; + } + + function resolveTarget(sources, index, propagate) { + var source = sources[index]; + var fill = source.fill; + var visited = [index]; + var target; + + if (!propagate) { + return fill; + } + + while (fill !== false && visited.indexOf(fill) === -1) { + if (!isFinite(fill)) { + return fill; + } + + target = sources[fill]; + if (!target) { + return false; + } + + if (target.visible) { + return fill; + } + + visited.push(fill); + fill = target.fill; + } + + return false; + } + + function createMapper(source) { + var fill = source.fill; + var type = 'dataset'; + + if (fill === false) { + return null; + } + + if (!isFinite(fill)) { + type = 'boundary'; + } + + return mappers[type](source); + } + + function isDrawable(point) { + return point && !point.skip; + } + + function drawArea(ctx, curve0, curve1, len0, len1) { + var i; + + if (!len0 || !len1) { + return; + } + + // building first area curve (normal) + ctx.moveTo(curve0[0].x, curve0[0].y); + for (i = 1; i < len0; ++i) { + helpers.canvas.lineTo(ctx, curve0[i - 1], curve0[i]); + } + + // joining the two area curves + ctx.lineTo(curve1[len1 - 1].x, curve1[len1 - 1].y); + + // building opposite area curve (reverse) + for (i = len1 - 1; i > 0; --i) { + helpers.canvas.lineTo(ctx, curve1[i], curve1[i - 1], true); + } + } + + function doFill(ctx, points, mapper, view, color, loop) { + var count = points.length; + var span = view.spanGaps; + var curve0 = []; + var curve1 = []; + var len0 = 0; + var len1 = 0; + var i, ilen, index, p0, p1, d0, d1; + + ctx.beginPath(); + + for (i = 0, ilen = (count + !!loop); i < ilen; ++i) { + index = i % count; + p0 = points[index]._view; + p1 = mapper(p0, index, view); + d0 = isDrawable(p0); + d1 = isDrawable(p1); + + if (d0 && d1) { + len0 = curve0.push(p0); + len1 = curve1.push(p1); + } else if (len0 && len1) { + if (!span) { + drawArea(ctx, curve0, curve1, len0, len1); + len0 = len1 = 0; + curve0 = []; + curve1 = []; + } else { + if (d0) { + curve0.push(p0); + } + if (d1) { + curve1.push(p1); + } + } + } + } + + drawArea(ctx, curve0, curve1, len0, len1); + + ctx.closePath(); + ctx.fillStyle = color; + ctx.fill(); + } + + return { + id: 'filler', + + afterDatasetsUpdate: function(chart, options) { + var count = (chart.data.datasets || []).length; + var propagate = options.propagate; + var sources = []; + var meta, i, el, source; + + for (i = 0; i < count; ++i) { + meta = chart.getDatasetMeta(i); + el = meta.dataset; + source = null; + + if (el && el._model && el instanceof elements.Line) { + source = { + visible: chart.isDatasetVisible(i), + fill: decodeFill(el, i, count), + chart: chart, + el: el + }; + } + + meta.$filler = source; + sources.push(source); + } + + for (i = 0; i < count; ++i) { + source = sources[i]; + if (!source) { + continue; + } + + source.fill = resolveTarget(sources, i, propagate); + source.boundary = computeBoundary(source); + source.mapper = createMapper(source); + } + }, + + beforeDatasetDraw: function(chart, args) { + var meta = args.meta.$filler; + if (!meta) { + return; + } + + var ctx = chart.ctx; + var el = meta.el; + var view = el._view; + var points = el._children || []; + var mapper = meta.mapper; + var color = view.backgroundColor || defaults.global.defaultColor; + + if (mapper && color && points.length) { + helpers.canvas.clipArea(ctx, chart.chartArea); + doFill(ctx, points, mapper, view, color, el._loop); + helpers.canvas.unclipArea(ctx); + } + } + }; +}; + +},{"25":25,"40":40,"45":45}],50:[function(require,module,exports){ +'use strict'; + +var defaults = require(25); +var Element = require(26); +var helpers = require(45); + +defaults._set('global', { + legend: { + display: true, + position: 'top', + fullWidth: true, + reverse: false, + weight: 1000, + + // a callback that will handle + onClick: function(e, legendItem) { + var index = legendItem.datasetIndex; + var ci = this.chart; + var meta = ci.getDatasetMeta(index); + + // See controller.isDatasetVisible comment + meta.hidden = meta.hidden === null ? !ci.data.datasets[index].hidden : null; + + // We hid a dataset ... rerender the chart + ci.update(); + }, + + onHover: null, + + labels: { + boxWidth: 40, + padding: 10, + // Generates labels shown in the legend + // Valid properties to return: + // text : text to display + // fillStyle : fill of coloured box + // strokeStyle: stroke of coloured box + // hidden : if this legend item refers to a hidden item + // lineCap : cap style for line + // lineDash + // lineDashOffset : + // lineJoin : + // lineWidth : + generateLabels: function(chart) { + var data = chart.data; + return helpers.isArray(data.datasets) ? data.datasets.map(function(dataset, i) { + return { + text: dataset.label, + fillStyle: (!helpers.isArray(dataset.backgroundColor) ? dataset.backgroundColor : dataset.backgroundColor[0]), + hidden: !chart.isDatasetVisible(i), + lineCap: dataset.borderCapStyle, + lineDash: dataset.borderDash, + lineDashOffset: dataset.borderDashOffset, + lineJoin: dataset.borderJoinStyle, + lineWidth: dataset.borderWidth, + strokeStyle: dataset.borderColor, + pointStyle: dataset.pointStyle, + + // Below is extra data used for toggling the datasets + datasetIndex: i + }; + }, this) : []; + } + } + }, + + legendCallback: function(chart) { + var text = []; + text.push('
    '); + for (var i = 0; i < chart.data.datasets.length; i++) { + text.push('
  • '); + if (chart.data.datasets[i].label) { + text.push(chart.data.datasets[i].label); + } + text.push('
  • '); + } + text.push('
'); + return text.join(''); + } +}); + +module.exports = function(Chart) { + + var layout = Chart.layoutService; + var noop = helpers.noop; + + /** + * Helper function to get the box width based on the usePointStyle option + * @param labelopts {Object} the label options on the legend + * @param fontSize {Number} the label font size + * @return {Number} width of the color box area + */ + function getBoxWidth(labelOpts, fontSize) { + return labelOpts.usePointStyle ? + fontSize * Math.SQRT2 : + labelOpts.boxWidth; + } + + Chart.Legend = Element.extend({ + + initialize: function(config) { + helpers.extend(this, config); + + // Contains hit boxes for each dataset (in dataset order) + this.legendHitBoxes = []; + + // Are we in doughnut mode which has a different data type + this.doughnutMode = false; + }, + + // These methods are ordered by lifecycle. Utilities then follow. + // Any function defined here is inherited by all legend types. + // Any function can be extended by the legend type + + beforeUpdate: noop, + update: function(maxWidth, maxHeight, margins) { + var me = this; + + // Update Lifecycle - Probably don't want to ever extend or overwrite this function ;) + me.beforeUpdate(); + + // Absorb the master measurements + me.maxWidth = maxWidth; + me.maxHeight = maxHeight; + me.margins = margins; + + // Dimensions + me.beforeSetDimensions(); + me.setDimensions(); + me.afterSetDimensions(); + // Labels + me.beforeBuildLabels(); + me.buildLabels(); + me.afterBuildLabels(); + + // Fit + me.beforeFit(); + me.fit(); + me.afterFit(); + // + me.afterUpdate(); + + return me.minSize; + }, + afterUpdate: noop, + + // + + beforeSetDimensions: noop, + setDimensions: function() { + var me = this; + // Set the unconstrained dimension before label rotation + if (me.isHorizontal()) { + // Reset position before calculating rotation + me.width = me.maxWidth; + me.left = 0; + me.right = me.width; + } else { + me.height = me.maxHeight; + + // Reset position before calculating rotation + me.top = 0; + me.bottom = me.height; + } + + // Reset padding + me.paddingLeft = 0; + me.paddingTop = 0; + me.paddingRight = 0; + me.paddingBottom = 0; + + // Reset minSize + me.minSize = { + width: 0, + height: 0 + }; + }, + afterSetDimensions: noop, + + // + + beforeBuildLabels: noop, + buildLabels: function() { + var me = this; + var labelOpts = me.options.labels || {}; + var legendItems = helpers.callback(labelOpts.generateLabels, [me.chart], me) || []; + + if (labelOpts.filter) { + legendItems = legendItems.filter(function(item) { + return labelOpts.filter(item, me.chart.data); + }); + } + + if (me.options.reverse) { + legendItems.reverse(); + } + + me.legendItems = legendItems; + }, + afterBuildLabels: noop, + + // + + beforeFit: noop, + fit: function() { + var me = this; + var opts = me.options; + var labelOpts = opts.labels; + var display = opts.display; + + var ctx = me.ctx; + + var globalDefault = defaults.global; + var valueOrDefault = helpers.valueOrDefault; + var fontSize = valueOrDefault(labelOpts.fontSize, globalDefault.defaultFontSize); + var fontStyle = valueOrDefault(labelOpts.fontStyle, globalDefault.defaultFontStyle); + var fontFamily = valueOrDefault(labelOpts.fontFamily, globalDefault.defaultFontFamily); + var labelFont = helpers.fontString(fontSize, fontStyle, fontFamily); + + // Reset hit boxes + var hitboxes = me.legendHitBoxes = []; + + var minSize = me.minSize; + var isHorizontal = me.isHorizontal(); + + if (isHorizontal) { + minSize.width = me.maxWidth; // fill all the width + minSize.height = display ? 10 : 0; + } else { + minSize.width = display ? 10 : 0; + minSize.height = me.maxHeight; // fill all the height + } + + // Increase sizes here + if (display) { + ctx.font = labelFont; + + if (isHorizontal) { + // Labels + + // Width of each line of legend boxes. Labels wrap onto multiple lines when there are too many to fit on one + var lineWidths = me.lineWidths = [0]; + var totalHeight = me.legendItems.length ? fontSize + (labelOpts.padding) : 0; + + ctx.textAlign = 'left'; + ctx.textBaseline = 'top'; + + helpers.each(me.legendItems, function(legendItem, i) { + var boxWidth = getBoxWidth(labelOpts, fontSize); + var width = boxWidth + (fontSize / 2) + ctx.measureText(legendItem.text).width; + + if (lineWidths[lineWidths.length - 1] + width + labelOpts.padding >= me.width) { + totalHeight += fontSize + (labelOpts.padding); + lineWidths[lineWidths.length] = me.left; + } + + // Store the hitbox width and height here. Final position will be updated in `draw` + hitboxes[i] = { + left: 0, + top: 0, + width: width, + height: fontSize + }; + + lineWidths[lineWidths.length - 1] += width + labelOpts.padding; + }); + + minSize.height += totalHeight; + + } else { + var vPadding = labelOpts.padding; + var columnWidths = me.columnWidths = []; + var totalWidth = labelOpts.padding; + var currentColWidth = 0; + var currentColHeight = 0; + var itemHeight = fontSize + vPadding; + + helpers.each(me.legendItems, function(legendItem, i) { + var boxWidth = getBoxWidth(labelOpts, fontSize); + var itemWidth = boxWidth + (fontSize / 2) + ctx.measureText(legendItem.text).width; + + // If too tall, go to new column + if (currentColHeight + itemHeight > minSize.height) { + totalWidth += currentColWidth + labelOpts.padding; + columnWidths.push(currentColWidth); // previous column width + + currentColWidth = 0; + currentColHeight = 0; + } + + // Get max width + currentColWidth = Math.max(currentColWidth, itemWidth); + currentColHeight += itemHeight; + + // Store the hitbox width and height here. Final position will be updated in `draw` + hitboxes[i] = { + left: 0, + top: 0, + width: itemWidth, + height: fontSize + }; + }); + + totalWidth += currentColWidth; + columnWidths.push(currentColWidth); + minSize.width += totalWidth; + } + } + + me.width = minSize.width; + me.height = minSize.height; + }, + afterFit: noop, + + // Shared Methods + isHorizontal: function() { + return this.options.position === 'top' || this.options.position === 'bottom'; + }, + + // Actually draw the legend on the canvas + draw: function() { + var me = this; + var opts = me.options; + var labelOpts = opts.labels; + var globalDefault = defaults.global; + var lineDefault = globalDefault.elements.line; + var legendWidth = me.width; + var lineWidths = me.lineWidths; + + if (opts.display) { + var ctx = me.ctx; + var valueOrDefault = helpers.valueOrDefault; + var fontColor = valueOrDefault(labelOpts.fontColor, globalDefault.defaultFontColor); + var fontSize = valueOrDefault(labelOpts.fontSize, globalDefault.defaultFontSize); + var fontStyle = valueOrDefault(labelOpts.fontStyle, globalDefault.defaultFontStyle); + var fontFamily = valueOrDefault(labelOpts.fontFamily, globalDefault.defaultFontFamily); + var labelFont = helpers.fontString(fontSize, fontStyle, fontFamily); + var cursor; + + // Canvas setup + ctx.textAlign = 'left'; + ctx.textBaseline = 'middle'; + ctx.lineWidth = 0.5; + ctx.strokeStyle = fontColor; // for strikethrough effect + ctx.fillStyle = fontColor; // render in correct colour + ctx.font = labelFont; + + var boxWidth = getBoxWidth(labelOpts, fontSize); + var hitboxes = me.legendHitBoxes; + + // current position + var drawLegendBox = function(x, y, legendItem) { + if (isNaN(boxWidth) || boxWidth <= 0) { + return; + } + + // Set the ctx for the box + ctx.save(); + + ctx.fillStyle = valueOrDefault(legendItem.fillStyle, globalDefault.defaultColor); + ctx.lineCap = valueOrDefault(legendItem.lineCap, lineDefault.borderCapStyle); + ctx.lineDashOffset = valueOrDefault(legendItem.lineDashOffset, lineDefault.borderDashOffset); + ctx.lineJoin = valueOrDefault(legendItem.lineJoin, lineDefault.borderJoinStyle); + ctx.lineWidth = valueOrDefault(legendItem.lineWidth, lineDefault.borderWidth); + ctx.strokeStyle = valueOrDefault(legendItem.strokeStyle, globalDefault.defaultColor); + var isLineWidthZero = (valueOrDefault(legendItem.lineWidth, lineDefault.borderWidth) === 0); + + if (ctx.setLineDash) { + // IE 9 and 10 do not support line dash + ctx.setLineDash(valueOrDefault(legendItem.lineDash, lineDefault.borderDash)); + } + + if (opts.labels && opts.labels.usePointStyle) { + // Recalculate x and y for drawPoint() because its expecting + // x and y to be center of figure (instead of top left) + var radius = fontSize * Math.SQRT2 / 2; + var offSet = radius / Math.SQRT2; + var centerX = x + offSet; + var centerY = y + offSet; + + // Draw pointStyle as legend symbol + helpers.canvas.drawPoint(ctx, legendItem.pointStyle, radius, centerX, centerY); + } else { + // Draw box as legend symbol + if (!isLineWidthZero) { + ctx.strokeRect(x, y, boxWidth, fontSize); + } + ctx.fillRect(x, y, boxWidth, fontSize); + } + + ctx.restore(); + }; + var fillText = function(x, y, legendItem, textWidth) { + var halfFontSize = fontSize / 2; + var xLeft = boxWidth + halfFontSize + x; + var yMiddle = y + halfFontSize; + + ctx.fillText(legendItem.text, xLeft, yMiddle); + + if (legendItem.hidden) { + // Strikethrough the text if hidden + ctx.beginPath(); + ctx.lineWidth = 2; + ctx.moveTo(xLeft, yMiddle); + ctx.lineTo(xLeft + textWidth, yMiddle); + ctx.stroke(); + } + }; + + // Horizontal + var isHorizontal = me.isHorizontal(); + if (isHorizontal) { + cursor = { + x: me.left + ((legendWidth - lineWidths[0]) / 2), + y: me.top + labelOpts.padding, + line: 0 + }; + } else { + cursor = { + x: me.left + labelOpts.padding, + y: me.top + labelOpts.padding, + line: 0 + }; + } + + var itemHeight = fontSize + labelOpts.padding; + helpers.each(me.legendItems, function(legendItem, i) { + var textWidth = ctx.measureText(legendItem.text).width; + var width = boxWidth + (fontSize / 2) + textWidth; + var x = cursor.x; + var y = cursor.y; + + if (isHorizontal) { + if (x + width >= legendWidth) { + y = cursor.y += itemHeight; + cursor.line++; + x = cursor.x = me.left + ((legendWidth - lineWidths[cursor.line]) / 2); + } + } else if (y + itemHeight > me.bottom) { + x = cursor.x = x + me.columnWidths[cursor.line] + labelOpts.padding; + y = cursor.y = me.top + labelOpts.padding; + cursor.line++; + } + + drawLegendBox(x, y, legendItem); + + hitboxes[i].left = x; + hitboxes[i].top = y; + + // Fill the actual label + fillText(x, y, legendItem, textWidth); + + if (isHorizontal) { + cursor.x += width + (labelOpts.padding); + } else { + cursor.y += itemHeight; + } + + }); + } + }, + + /** + * Handle an event + * @private + * @param {IEvent} event - The event to handle + * @return {Boolean} true if a change occured + */ + handleEvent: function(e) { + var me = this; + var opts = me.options; + var type = e.type === 'mouseup' ? 'click' : e.type; + var changed = false; + + if (type === 'mousemove') { + if (!opts.onHover) { + return; + } + } else if (type === 'click') { + if (!opts.onClick) { + return; + } + } else { + return; + } + + // Chart event already has relative position in it + var x = e.x; + var y = e.y; + + if (x >= me.left && x <= me.right && y >= me.top && y <= me.bottom) { + // See if we are touching one of the dataset boxes + var lh = me.legendHitBoxes; + for (var i = 0; i < lh.length; ++i) { + var hitBox = lh[i]; + + if (x >= hitBox.left && x <= hitBox.left + hitBox.width && y >= hitBox.top && y <= hitBox.top + hitBox.height) { + // Touching an element + if (type === 'click') { + // use e.native for backwards compatibility + opts.onClick.call(me, e.native, me.legendItems[i]); + changed = true; + break; + } else if (type === 'mousemove') { + // use e.native for backwards compatibility + opts.onHover.call(me, e.native, me.legendItems[i]); + changed = true; + break; + } + } + } + } + + return changed; + } + }); + + function createNewLegendAndAttach(chart, legendOpts) { + var legend = new Chart.Legend({ + ctx: chart.ctx, + options: legendOpts, + chart: chart + }); + + layout.configure(chart, legend, legendOpts); + layout.addBox(chart, legend); + chart.legend = legend; + } + + return { + id: 'legend', + + beforeInit: function(chart) { + var legendOpts = chart.options.legend; + + if (legendOpts) { + createNewLegendAndAttach(chart, legendOpts); + } + }, + + beforeUpdate: function(chart) { + var legendOpts = chart.options.legend; + var legend = chart.legend; + + if (legendOpts) { + helpers.mergeIf(legendOpts, defaults.global.legend); + + if (legend) { + layout.configure(chart, legend, legendOpts); + legend.options = legendOpts; + } else { + createNewLegendAndAttach(chart, legendOpts); + } + } else if (legend) { + layout.removeBox(chart, legend); + delete chart.legend; + } + }, + + afterEvent: function(chart, e) { + var legend = chart.legend; + if (legend) { + legend.handleEvent(e); + } + } + }; +}; + +},{"25":25,"26":26,"45":45}],51:[function(require,module,exports){ +'use strict'; + +var defaults = require(25); +var Element = require(26); +var helpers = require(45); + +defaults._set('global', { + title: { + display: false, + fontStyle: 'bold', + fullWidth: true, + lineHeight: 1.2, + padding: 10, + position: 'top', + text: '', + weight: 2000 // by default greater than legend (1000) to be above + } +}); + +module.exports = function(Chart) { + + var layout = Chart.layoutService; + var noop = helpers.noop; + + Chart.Title = Element.extend({ + initialize: function(config) { + var me = this; + helpers.extend(me, config); + + // Contains hit boxes for each dataset (in dataset order) + me.legendHitBoxes = []; + }, + + // These methods are ordered by lifecycle. Utilities then follow. + + beforeUpdate: noop, + update: function(maxWidth, maxHeight, margins) { + var me = this; + + // Update Lifecycle - Probably don't want to ever extend or overwrite this function ;) + me.beforeUpdate(); + + // Absorb the master measurements + me.maxWidth = maxWidth; + me.maxHeight = maxHeight; + me.margins = margins; + + // Dimensions + me.beforeSetDimensions(); + me.setDimensions(); + me.afterSetDimensions(); + // Labels + me.beforeBuildLabels(); + me.buildLabels(); + me.afterBuildLabels(); + + // Fit + me.beforeFit(); + me.fit(); + me.afterFit(); + // + me.afterUpdate(); + + return me.minSize; + + }, + afterUpdate: noop, + + // + + beforeSetDimensions: noop, + setDimensions: function() { + var me = this; + // Set the unconstrained dimension before label rotation + if (me.isHorizontal()) { + // Reset position before calculating rotation + me.width = me.maxWidth; + me.left = 0; + me.right = me.width; + } else { + me.height = me.maxHeight; + + // Reset position before calculating rotation + me.top = 0; + me.bottom = me.height; + } + + // Reset padding + me.paddingLeft = 0; + me.paddingTop = 0; + me.paddingRight = 0; + me.paddingBottom = 0; + + // Reset minSize + me.minSize = { + width: 0, + height: 0 + }; + }, + afterSetDimensions: noop, + + // + + beforeBuildLabels: noop, + buildLabels: noop, + afterBuildLabels: noop, + + // + + beforeFit: noop, + fit: function() { + var me = this; + var valueOrDefault = helpers.valueOrDefault; + var opts = me.options; + var display = opts.display; + var fontSize = valueOrDefault(opts.fontSize, defaults.global.defaultFontSize); + var minSize = me.minSize; + var lineCount = helpers.isArray(opts.text) ? opts.text.length : 1; + var lineHeight = helpers.options.toLineHeight(opts.lineHeight, fontSize); + var textSize = display ? (lineCount * lineHeight) + (opts.padding * 2) : 0; + + if (me.isHorizontal()) { + minSize.width = me.maxWidth; // fill all the width + minSize.height = textSize; + } else { + minSize.width = textSize; + minSize.height = me.maxHeight; // fill all the height + } + + me.width = minSize.width; + me.height = minSize.height; + + }, + afterFit: noop, + + // Shared Methods + isHorizontal: function() { + var pos = this.options.position; + return pos === 'top' || pos === 'bottom'; + }, + + // Actually draw the title block on the canvas + draw: function() { + var me = this; + var ctx = me.ctx; + var valueOrDefault = helpers.valueOrDefault; + var opts = me.options; + var globalDefaults = defaults.global; + + if (opts.display) { + var fontSize = valueOrDefault(opts.fontSize, globalDefaults.defaultFontSize); + var fontStyle = valueOrDefault(opts.fontStyle, globalDefaults.defaultFontStyle); + var fontFamily = valueOrDefault(opts.fontFamily, globalDefaults.defaultFontFamily); + var titleFont = helpers.fontString(fontSize, fontStyle, fontFamily); + var lineHeight = helpers.options.toLineHeight(opts.lineHeight, fontSize); + var offset = lineHeight / 2 + opts.padding; + var rotation = 0; + var top = me.top; + var left = me.left; + var bottom = me.bottom; + var right = me.right; + var maxWidth, titleX, titleY; + + ctx.fillStyle = valueOrDefault(opts.fontColor, globalDefaults.defaultFontColor); // render in correct colour + ctx.font = titleFont; + + // Horizontal + if (me.isHorizontal()) { + titleX = left + ((right - left) / 2); // midpoint of the width + titleY = top + offset; + maxWidth = right - left; + } else { + titleX = opts.position === 'left' ? left + offset : right - offset; + titleY = top + ((bottom - top) / 2); + maxWidth = bottom - top; + rotation = Math.PI * (opts.position === 'left' ? -0.5 : 0.5); + } + + ctx.save(); + ctx.translate(titleX, titleY); + ctx.rotate(rotation); + ctx.textAlign = 'center'; + ctx.textBaseline = 'middle'; + + var text = opts.text; + if (helpers.isArray(text)) { + var y = 0; + for (var i = 0; i < text.length; ++i) { + ctx.fillText(text[i], 0, y, maxWidth); + y += lineHeight; + } + } else { + ctx.fillText(text, 0, 0, maxWidth); + } + + ctx.restore(); + } + } + }); + + function createNewTitleBlockAndAttach(chart, titleOpts) { + var title = new Chart.Title({ + ctx: chart.ctx, + options: titleOpts, + chart: chart + }); + + layout.configure(chart, title, titleOpts); + layout.addBox(chart, title); + chart.titleBlock = title; + } + + return { + id: 'title', + + beforeInit: function(chart) { + var titleOpts = chart.options.title; + + if (titleOpts) { + createNewTitleBlockAndAttach(chart, titleOpts); + } + }, + + beforeUpdate: function(chart) { + var titleOpts = chart.options.title; + var titleBlock = chart.titleBlock; + + if (titleOpts) { + helpers.mergeIf(titleOpts, defaults.global.title); + + if (titleBlock) { + layout.configure(chart, titleBlock, titleOpts); + titleBlock.options = titleOpts; + } else { + createNewTitleBlockAndAttach(chart, titleOpts); + } + } else if (titleBlock) { + Chart.layoutService.removeBox(chart, titleBlock); + delete chart.titleBlock; + } + } + }; +}; + +},{"25":25,"26":26,"45":45}],52:[function(require,module,exports){ +'use strict'; + +module.exports = function(Chart) { + + // Default config for a category scale + var defaultConfig = { + position: 'bottom' + }; + + var DatasetScale = Chart.Scale.extend({ + /** + * Internal function to get the correct labels. If data.xLabels or data.yLabels are defined, use those + * else fall back to data.labels + * @private + */ + getLabels: function() { + var data = this.chart.data; + return this.options.labels || (this.isHorizontal() ? data.xLabels : data.yLabels) || data.labels; + }, + + determineDataLimits: function() { + var me = this; + var labels = me.getLabels(); + me.minIndex = 0; + me.maxIndex = labels.length - 1; + var findIndex; + + if (me.options.ticks.min !== undefined) { + // user specified min value + findIndex = labels.indexOf(me.options.ticks.min); + me.minIndex = findIndex !== -1 ? findIndex : me.minIndex; + } + + if (me.options.ticks.max !== undefined) { + // user specified max value + findIndex = labels.indexOf(me.options.ticks.max); + me.maxIndex = findIndex !== -1 ? findIndex : me.maxIndex; + } + + me.min = labels[me.minIndex]; + me.max = labels[me.maxIndex]; + }, + + buildTicks: function() { + var me = this; + var labels = me.getLabels(); + // If we are viewing some subset of labels, slice the original array + me.ticks = (me.minIndex === 0 && me.maxIndex === labels.length - 1) ? labels : labels.slice(me.minIndex, me.maxIndex + 1); + }, + + getLabelForIndex: function(index, datasetIndex) { + var me = this; + var data = me.chart.data; + var isHorizontal = me.isHorizontal(); + + if (data.yLabels && !isHorizontal) { + return me.getRightValue(data.datasets[datasetIndex].data[index]); + } + return me.ticks[index - me.minIndex]; + }, + + // Used to get data value locations. Value can either be an index or a numerical value + getPixelForValue: function(value, index) { + var me = this; + var offset = me.options.offset; + // 1 is added because we need the length but we have the indexes + var offsetAmt = Math.max((me.maxIndex + 1 - me.minIndex - (offset ? 0 : 1)), 1); + + // If value is a data object, then index is the index in the data array, + // not the index of the scale. We need to change that. + var valueCategory; + if (value !== undefined && value !== null) { + valueCategory = me.isHorizontal() ? value.x : value.y; + } + if (valueCategory !== undefined || (value !== undefined && isNaN(index))) { + var labels = me.getLabels(); + value = valueCategory || value; + var idx = labels.indexOf(value); + index = idx !== -1 ? idx : index; + } + + if (me.isHorizontal()) { + var valueWidth = me.width / offsetAmt; + var widthOffset = (valueWidth * (index - me.minIndex)); + + if (offset) { + widthOffset += (valueWidth / 2); + } + + return me.left + Math.round(widthOffset); + } + var valueHeight = me.height / offsetAmt; + var heightOffset = (valueHeight * (index - me.minIndex)); + + if (offset) { + heightOffset += (valueHeight / 2); + } + + return me.top + Math.round(heightOffset); + }, + getPixelForTick: function(index) { + return this.getPixelForValue(this.ticks[index], index + this.minIndex, null); + }, + getValueForPixel: function(pixel) { + var me = this; + var offset = me.options.offset; + var value; + var offsetAmt = Math.max((me._ticks.length - (offset ? 0 : 1)), 1); + var horz = me.isHorizontal(); + var valueDimension = (horz ? me.width : me.height) / offsetAmt; + + pixel -= horz ? me.left : me.top; + + if (offset) { + pixel -= (valueDimension / 2); + } + + if (pixel <= 0) { + value = 0; + } else { + value = Math.round(pixel / valueDimension); + } + + return value + me.minIndex; + }, + getBasePixel: function() { + return this.bottom; + } + }); + + Chart.scaleService.registerScaleType('category', DatasetScale, defaultConfig); + +}; + +},{}],53:[function(require,module,exports){ +'use strict'; + +var defaults = require(25); +var helpers = require(45); +var Ticks = require(34); + +module.exports = function(Chart) { + + var defaultConfig = { + position: 'left', + ticks: { + callback: Ticks.formatters.linear + } + }; + + var LinearScale = Chart.LinearScaleBase.extend({ + + determineDataLimits: function() { + var me = this; + var opts = me.options; + var chart = me.chart; + var data = chart.data; + var datasets = data.datasets; + var isHorizontal = me.isHorizontal(); + var DEFAULT_MIN = 0; + var DEFAULT_MAX = 1; + + function IDMatches(meta) { + return isHorizontal ? meta.xAxisID === me.id : meta.yAxisID === me.id; + } + + // First Calculate the range + me.min = null; + me.max = null; + + var hasStacks = opts.stacked; + if (hasStacks === undefined) { + helpers.each(datasets, function(dataset, datasetIndex) { + if (hasStacks) { + return; + } + + var meta = chart.getDatasetMeta(datasetIndex); + if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta) && + meta.stack !== undefined) { + hasStacks = true; + } + }); + } + + if (opts.stacked || hasStacks) { + var valuesPerStack = {}; + + helpers.each(datasets, function(dataset, datasetIndex) { + var meta = chart.getDatasetMeta(datasetIndex); + var key = [ + meta.type, + // we have a separate stack for stack=undefined datasets when the opts.stacked is undefined + ((opts.stacked === undefined && meta.stack === undefined) ? datasetIndex : ''), + meta.stack + ].join('.'); + + if (valuesPerStack[key] === undefined) { + valuesPerStack[key] = { + positiveValues: [], + negativeValues: [] + }; + } + + // Store these per type + var positiveValues = valuesPerStack[key].positiveValues; + var negativeValues = valuesPerStack[key].negativeValues; + + if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta)) { + helpers.each(dataset.data, function(rawValue, index) { + var value = +me.getRightValue(rawValue); + if (isNaN(value) || meta.data[index].hidden) { + return; + } + + positiveValues[index] = positiveValues[index] || 0; + negativeValues[index] = negativeValues[index] || 0; + + if (opts.relativePoints) { + positiveValues[index] = 100; + } else if (value < 0) { + negativeValues[index] += value; + } else { + positiveValues[index] += value; + } + }); + } + }); + + helpers.each(valuesPerStack, function(valuesForType) { + var values = valuesForType.positiveValues.concat(valuesForType.negativeValues); + var minVal = helpers.min(values); + var maxVal = helpers.max(values); + me.min = me.min === null ? minVal : Math.min(me.min, minVal); + me.max = me.max === null ? maxVal : Math.max(me.max, maxVal); + }); + + } else { + helpers.each(datasets, function(dataset, datasetIndex) { + var meta = chart.getDatasetMeta(datasetIndex); + if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta)) { + helpers.each(dataset.data, function(rawValue, index) { + var value = +me.getRightValue(rawValue); + if (isNaN(value) || meta.data[index].hidden) { + return; + } + + if (me.min === null) { + me.min = value; + } else if (value < me.min) { + me.min = value; + } + + if (me.max === null) { + me.max = value; + } else if (value > me.max) { + me.max = value; + } + }); + } + }); + } + + me.min = isFinite(me.min) && !isNaN(me.min) ? me.min : DEFAULT_MIN; + me.max = isFinite(me.max) && !isNaN(me.max) ? me.max : DEFAULT_MAX; + + // Common base implementation to handle ticks.min, ticks.max, ticks.beginAtZero + this.handleTickRangeOptions(); + }, + getTickLimit: function() { + var maxTicks; + var me = this; + var tickOpts = me.options.ticks; + + if (me.isHorizontal()) { + maxTicks = Math.min(tickOpts.maxTicksLimit ? tickOpts.maxTicksLimit : 11, Math.ceil(me.width / 50)); + } else { + // The factor of 2 used to scale the font size has been experimentally determined. + var tickFontSize = helpers.valueOrDefault(tickOpts.fontSize, defaults.global.defaultFontSize); + maxTicks = Math.min(tickOpts.maxTicksLimit ? tickOpts.maxTicksLimit : 11, Math.ceil(me.height / (2 * tickFontSize))); + } + + return maxTicks; + }, + // Called after the ticks are built. We need + handleDirectionalChanges: function() { + if (!this.isHorizontal()) { + // We are in a vertical orientation. The top value is the highest. So reverse the array + this.ticks.reverse(); + } + }, + getLabelForIndex: function(index, datasetIndex) { + return +this.getRightValue(this.chart.data.datasets[datasetIndex].data[index]); + }, + // Utils + getPixelForValue: function(value) { + // This must be called after fit has been run so that + // this.left, this.top, this.right, and this.bottom have been defined + var me = this; + var start = me.start; + + var rightValue = +me.getRightValue(value); + var pixel; + var range = me.end - start; + + if (me.isHorizontal()) { + pixel = me.left + (me.width / range * (rightValue - start)); + return Math.round(pixel); + } + + pixel = me.bottom - (me.height / range * (rightValue - start)); + return Math.round(pixel); + }, + getValueForPixel: function(pixel) { + var me = this; + var isHorizontal = me.isHorizontal(); + var innerDimension = isHorizontal ? me.width : me.height; + var offset = (isHorizontal ? pixel - me.left : me.bottom - pixel) / innerDimension; + return me.start + ((me.end - me.start) * offset); + }, + getPixelForTick: function(index) { + return this.getPixelForValue(this.ticksAsNumbers[index]); + } + }); + Chart.scaleService.registerScaleType('linear', LinearScale, defaultConfig); + +}; + +},{"25":25,"34":34,"45":45}],54:[function(require,module,exports){ +'use strict'; + +var helpers = require(45); +var Ticks = require(34); + +module.exports = function(Chart) { + + var noop = helpers.noop; + + Chart.LinearScaleBase = Chart.Scale.extend({ + getRightValue: function(value) { + if (typeof value === 'string') { + return +value; + } + return Chart.Scale.prototype.getRightValue.call(this, value); + }, + + handleTickRangeOptions: function() { + var me = this; + var opts = me.options; + var tickOpts = opts.ticks; + + // If we are forcing it to begin at 0, but 0 will already be rendered on the chart, + // do nothing since that would make the chart weird. If the user really wants a weird chart + // axis, they can manually override it + if (tickOpts.beginAtZero) { + var minSign = helpers.sign(me.min); + var maxSign = helpers.sign(me.max); + + if (minSign < 0 && maxSign < 0) { + // move the top up to 0 + me.max = 0; + } else if (minSign > 0 && maxSign > 0) { + // move the bottom down to 0 + me.min = 0; + } + } + + var setMin = tickOpts.min !== undefined || tickOpts.suggestedMin !== undefined; + var setMax = tickOpts.max !== undefined || tickOpts.suggestedMax !== undefined; + + if (tickOpts.min !== undefined) { + me.min = tickOpts.min; + } else if (tickOpts.suggestedMin !== undefined) { + if (me.min === null) { + me.min = tickOpts.suggestedMin; + } else { + me.min = Math.min(me.min, tickOpts.suggestedMin); + } + } + + if (tickOpts.max !== undefined) { + me.max = tickOpts.max; + } else if (tickOpts.suggestedMax !== undefined) { + if (me.max === null) { + me.max = tickOpts.suggestedMax; + } else { + me.max = Math.max(me.max, tickOpts.suggestedMax); + } + } + + if (setMin !== setMax) { + // We set the min or the max but not both. + // So ensure that our range is good + // Inverted or 0 length range can happen when + // ticks.min is set, and no datasets are visible + if (me.min >= me.max) { + if (setMin) { + me.max = me.min + 1; + } else { + me.min = me.max - 1; + } + } + } + + if (me.min === me.max) { + me.max++; + + if (!tickOpts.beginAtZero) { + me.min--; + } + } + }, + getTickLimit: noop, + handleDirectionalChanges: noop, + + buildTicks: function() { + var me = this; + var opts = me.options; + var tickOpts = opts.ticks; + + // Figure out what the max number of ticks we can support it is based on the size of + // the axis area. For now, we say that the minimum tick spacing in pixels must be 50 + // We also limit the maximum number of ticks to 11 which gives a nice 10 squares on + // the graph. Make sure we always have at least 2 ticks + var maxTicks = me.getTickLimit(); + maxTicks = Math.max(2, maxTicks); + + var numericGeneratorOptions = { + maxTicks: maxTicks, + min: tickOpts.min, + max: tickOpts.max, + stepSize: helpers.valueOrDefault(tickOpts.fixedStepSize, tickOpts.stepSize) + }; + var ticks = me.ticks = Ticks.generators.linear(numericGeneratorOptions, me); + + me.handleDirectionalChanges(); + + // At this point, we need to update our max and min given the tick values since we have expanded the + // range of the scale + me.max = helpers.max(ticks); + me.min = helpers.min(ticks); + + if (tickOpts.reverse) { + ticks.reverse(); + + me.start = me.max; + me.end = me.min; + } else { + me.start = me.min; + me.end = me.max; + } + }, + convertTicksToLabels: function() { + var me = this; + me.ticksAsNumbers = me.ticks.slice(); + me.zeroLineIndex = me.ticks.indexOf(0); + + Chart.Scale.prototype.convertTicksToLabels.call(me); + } + }); +}; + +},{"34":34,"45":45}],55:[function(require,module,exports){ +'use strict'; + +var helpers = require(45); +var Ticks = require(34); + +module.exports = function(Chart) { + + var defaultConfig = { + position: 'left', + + // label settings + ticks: { + callback: Ticks.formatters.logarithmic + } + }; + + var LogarithmicScale = Chart.Scale.extend({ + determineDataLimits: function() { + var me = this; + var opts = me.options; + var tickOpts = opts.ticks; + var chart = me.chart; + var data = chart.data; + var datasets = data.datasets; + var valueOrDefault = helpers.valueOrDefault; + var isHorizontal = me.isHorizontal(); + function IDMatches(meta) { + return isHorizontal ? meta.xAxisID === me.id : meta.yAxisID === me.id; + } + + // Calculate Range + me.min = null; + me.max = null; + me.minNotZero = null; + + var hasStacks = opts.stacked; + if (hasStacks === undefined) { + helpers.each(datasets, function(dataset, datasetIndex) { + if (hasStacks) { + return; + } + + var meta = chart.getDatasetMeta(datasetIndex); + if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta) && + meta.stack !== undefined) { + hasStacks = true; + } + }); + } + + if (opts.stacked || hasStacks) { + var valuesPerStack = {}; + + helpers.each(datasets, function(dataset, datasetIndex) { + var meta = chart.getDatasetMeta(datasetIndex); + var key = [ + meta.type, + // we have a separate stack for stack=undefined datasets when the opts.stacked is undefined + ((opts.stacked === undefined && meta.stack === undefined) ? datasetIndex : ''), + meta.stack + ].join('.'); + + if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta)) { + if (valuesPerStack[key] === undefined) { + valuesPerStack[key] = []; + } + + helpers.each(dataset.data, function(rawValue, index) { + var values = valuesPerStack[key]; + var value = +me.getRightValue(rawValue); + if (isNaN(value) || meta.data[index].hidden) { + return; + } + + values[index] = values[index] || 0; + + if (opts.relativePoints) { + values[index] = 100; + } else { + // Don't need to split positive and negative since the log scale can't handle a 0 crossing + values[index] += value; + } + }); + } + }); + + helpers.each(valuesPerStack, function(valuesForType) { + var minVal = helpers.min(valuesForType); + var maxVal = helpers.max(valuesForType); + me.min = me.min === null ? minVal : Math.min(me.min, minVal); + me.max = me.max === null ? maxVal : Math.max(me.max, maxVal); + }); + + } else { + helpers.each(datasets, function(dataset, datasetIndex) { + var meta = chart.getDatasetMeta(datasetIndex); + if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta)) { + helpers.each(dataset.data, function(rawValue, index) { + var value = +me.getRightValue(rawValue); + if (isNaN(value) || meta.data[index].hidden) { + return; + } + + if (me.min === null) { + me.min = value; + } else if (value < me.min) { + me.min = value; + } + + if (me.max === null) { + me.max = value; + } else if (value > me.max) { + me.max = value; + } + + if (value !== 0 && (me.minNotZero === null || value < me.minNotZero)) { + me.minNotZero = value; + } + }); + } + }); + } + + me.min = valueOrDefault(tickOpts.min, me.min); + me.max = valueOrDefault(tickOpts.max, me.max); + + if (me.min === me.max) { + if (me.min !== 0 && me.min !== null) { + me.min = Math.pow(10, Math.floor(helpers.log10(me.min)) - 1); + me.max = Math.pow(10, Math.floor(helpers.log10(me.max)) + 1); + } else { + me.min = 1; + me.max = 10; + } + } + }, + buildTicks: function() { + var me = this; + var opts = me.options; + var tickOpts = opts.ticks; + + var generationOptions = { + min: tickOpts.min, + max: tickOpts.max + }; + var ticks = me.ticks = Ticks.generators.logarithmic(generationOptions, me); + + if (!me.isHorizontal()) { + // We are in a vertical orientation. The top value is the highest. So reverse the array + ticks.reverse(); + } + + // At this point, we need to update our max and min given the tick values since we have expanded the + // range of the scale + me.max = helpers.max(ticks); + me.min = helpers.min(ticks); + + if (tickOpts.reverse) { + ticks.reverse(); + + me.start = me.max; + me.end = me.min; + } else { + me.start = me.min; + me.end = me.max; + } + }, + convertTicksToLabels: function() { + this.tickValues = this.ticks.slice(); + + Chart.Scale.prototype.convertTicksToLabels.call(this); + }, + // Get the correct tooltip label + getLabelForIndex: function(index, datasetIndex) { + return +this.getRightValue(this.chart.data.datasets[datasetIndex].data[index]); + }, + getPixelForTick: function(index) { + return this.getPixelForValue(this.tickValues[index]); + }, + getPixelForValue: function(value) { + var me = this; + var start = me.start; + var newVal = +me.getRightValue(value); + var opts = me.options; + var tickOpts = opts.ticks; + var innerDimension, pixel, range; + + if (me.isHorizontal()) { + range = helpers.log10(me.end) - helpers.log10(start); // todo: if start === 0 + if (newVal === 0) { + pixel = me.left; + } else { + innerDimension = me.width; + pixel = me.left + (innerDimension / range * (helpers.log10(newVal) - helpers.log10(start))); + } + } else { + // Bottom - top since pixels increase downward on a screen + innerDimension = me.height; + if (start === 0 && !tickOpts.reverse) { + range = helpers.log10(me.end) - helpers.log10(me.minNotZero); + if (newVal === start) { + pixel = me.bottom; + } else if (newVal === me.minNotZero) { + pixel = me.bottom - innerDimension * 0.02; + } else { + pixel = me.bottom - innerDimension * 0.02 - (innerDimension * 0.98 / range * (helpers.log10(newVal) - helpers.log10(me.minNotZero))); + } + } else if (me.end === 0 && tickOpts.reverse) { + range = helpers.log10(me.start) - helpers.log10(me.minNotZero); + if (newVal === me.end) { + pixel = me.top; + } else if (newVal === me.minNotZero) { + pixel = me.top + innerDimension * 0.02; + } else { + pixel = me.top + innerDimension * 0.02 + (innerDimension * 0.98 / range * (helpers.log10(newVal) - helpers.log10(me.minNotZero))); + } + } else if (newVal === 0) { + pixel = tickOpts.reverse ? me.top : me.bottom; + } else { + range = helpers.log10(me.end) - helpers.log10(start); + innerDimension = me.height; + pixel = me.bottom - (innerDimension / range * (helpers.log10(newVal) - helpers.log10(start))); + } + } + return pixel; + }, + getValueForPixel: function(pixel) { + var me = this; + var range = helpers.log10(me.end) - helpers.log10(me.start); + var value, innerDimension; + + if (me.isHorizontal()) { + innerDimension = me.width; + value = me.start * Math.pow(10, (pixel - me.left) * range / innerDimension); + } else { // todo: if start === 0 + innerDimension = me.height; + value = Math.pow(10, (me.bottom - pixel) * range / innerDimension) / me.start; + } + return value; + } + }); + Chart.scaleService.registerScaleType('logarithmic', LogarithmicScale, defaultConfig); + +}; + +},{"34":34,"45":45}],56:[function(require,module,exports){ +'use strict'; + +var defaults = require(25); +var helpers = require(45); +var Ticks = require(34); + +module.exports = function(Chart) { + + var globalDefaults = defaults.global; + + var defaultConfig = { + display: true, + + // Boolean - Whether to animate scaling the chart from the centre + animate: true, + position: 'chartArea', + + angleLines: { + display: true, + color: 'rgba(0, 0, 0, 0.1)', + lineWidth: 1 + }, + + gridLines: { + circular: false + }, + + // label settings + ticks: { + // Boolean - Show a backdrop to the scale label + showLabelBackdrop: true, + + // String - The colour of the label backdrop + backdropColor: 'rgba(255,255,255,0.75)', + + // Number - The backdrop padding above & below the label in pixels + backdropPaddingY: 2, + + // Number - The backdrop padding to the side of the label in pixels + backdropPaddingX: 2, + + callback: Ticks.formatters.linear + }, + + pointLabels: { + // Boolean - if true, show point labels + display: true, + + // Number - Point label font size in pixels + fontSize: 10, + + // Function - Used to convert point labels + callback: function(label) { + return label; + } + } + }; + + function getValueCount(scale) { + var opts = scale.options; + return opts.angleLines.display || opts.pointLabels.display ? scale.chart.data.labels.length : 0; + } + + function getPointLabelFontOptions(scale) { + var pointLabelOptions = scale.options.pointLabels; + var fontSize = helpers.valueOrDefault(pointLabelOptions.fontSize, globalDefaults.defaultFontSize); + var fontStyle = helpers.valueOrDefault(pointLabelOptions.fontStyle, globalDefaults.defaultFontStyle); + var fontFamily = helpers.valueOrDefault(pointLabelOptions.fontFamily, globalDefaults.defaultFontFamily); + var font = helpers.fontString(fontSize, fontStyle, fontFamily); + + return { + size: fontSize, + style: fontStyle, + family: fontFamily, + font: font + }; + } + + function measureLabelSize(ctx, fontSize, label) { + if (helpers.isArray(label)) { + return { + w: helpers.longestText(ctx, ctx.font, label), + h: (label.length * fontSize) + ((label.length - 1) * 1.5 * fontSize) + }; + } + + return { + w: ctx.measureText(label).width, + h: fontSize + }; + } + + function determineLimits(angle, pos, size, min, max) { + if (angle === min || angle === max) { + return { + start: pos - (size / 2), + end: pos + (size / 2) + }; + } else if (angle < min || angle > max) { + return { + start: pos - size - 5, + end: pos + }; + } + + return { + start: pos, + end: pos + size + 5 + }; + } + + /** + * Helper function to fit a radial linear scale with point labels + */ + function fitWithPointLabels(scale) { + /* + * Right, this is really confusing and there is a lot of maths going on here + * The gist of the problem is here: https://gist.github.com/nnnick/696cc9c55f4b0beb8fe9 + * + * Reaction: https://dl.dropboxusercontent.com/u/34601363/toomuchscience.gif + * + * Solution: + * + * We assume the radius of the polygon is half the size of the canvas at first + * at each index we check if the text overlaps. + * + * Where it does, we store that angle and that index. + * + * After finding the largest index and angle we calculate how much we need to remove + * from the shape radius to move the point inwards by that x. + * + * We average the left and right distances to get the maximum shape radius that can fit in the box + * along with labels. + * + * Once we have that, we can find the centre point for the chart, by taking the x text protrusion + * on each side, removing that from the size, halving it and adding the left x protrusion width. + * + * This will mean we have a shape fitted to the canvas, as large as it can be with the labels + * and position it in the most space efficient manner + * + * https://dl.dropboxusercontent.com/u/34601363/yeahscience.gif + */ + + var plFont = getPointLabelFontOptions(scale); + + // Get maximum radius of the polygon. Either half the height (minus the text width) or half the width. + // Use this to calculate the offset + change. - Make sure L/R protrusion is at least 0 to stop issues with centre points + var largestPossibleRadius = Math.min(scale.height / 2, scale.width / 2); + var furthestLimits = { + r: scale.width, + l: 0, + t: scale.height, + b: 0 + }; + var furthestAngles = {}; + var i, textSize, pointPosition; + + scale.ctx.font = plFont.font; + scale._pointLabelSizes = []; + + var valueCount = getValueCount(scale); + for (i = 0; i < valueCount; i++) { + pointPosition = scale.getPointPosition(i, largestPossibleRadius); + textSize = measureLabelSize(scale.ctx, plFont.size, scale.pointLabels[i] || ''); + scale._pointLabelSizes[i] = textSize; + + // Add quarter circle to make degree 0 mean top of circle + var angleRadians = scale.getIndexAngle(i); + var angle = helpers.toDegrees(angleRadians) % 360; + var hLimits = determineLimits(angle, pointPosition.x, textSize.w, 0, 180); + var vLimits = determineLimits(angle, pointPosition.y, textSize.h, 90, 270); + + if (hLimits.start < furthestLimits.l) { + furthestLimits.l = hLimits.start; + furthestAngles.l = angleRadians; + } + + if (hLimits.end > furthestLimits.r) { + furthestLimits.r = hLimits.end; + furthestAngles.r = angleRadians; + } + + if (vLimits.start < furthestLimits.t) { + furthestLimits.t = vLimits.start; + furthestAngles.t = angleRadians; + } + + if (vLimits.end > furthestLimits.b) { + furthestLimits.b = vLimits.end; + furthestAngles.b = angleRadians; + } + } + + scale.setReductions(largestPossibleRadius, furthestLimits, furthestAngles); + } + + /** + * Helper function to fit a radial linear scale with no point labels + */ + function fit(scale) { + var largestPossibleRadius = Math.min(scale.height / 2, scale.width / 2); + scale.drawingArea = Math.round(largestPossibleRadius); + scale.setCenterPoint(0, 0, 0, 0); + } + + function getTextAlignForAngle(angle) { + if (angle === 0 || angle === 180) { + return 'center'; + } else if (angle < 180) { + return 'left'; + } + + return 'right'; + } + + function fillText(ctx, text, position, fontSize) { + if (helpers.isArray(text)) { + var y = position.y; + var spacing = 1.5 * fontSize; + + for (var i = 0; i < text.length; ++i) { + ctx.fillText(text[i], position.x, y); + y += spacing; + } + } else { + ctx.fillText(text, position.x, position.y); + } + } + + function adjustPointPositionForLabelHeight(angle, textSize, position) { + if (angle === 90 || angle === 270) { + position.y -= (textSize.h / 2); + } else if (angle > 270 || angle < 90) { + position.y -= textSize.h; + } + } + + function drawPointLabels(scale) { + var ctx = scale.ctx; + var valueOrDefault = helpers.valueOrDefault; + var opts = scale.options; + var angleLineOpts = opts.angleLines; + var pointLabelOpts = opts.pointLabels; + + ctx.lineWidth = angleLineOpts.lineWidth; + ctx.strokeStyle = angleLineOpts.color; + + var outerDistance = scale.getDistanceFromCenterForValue(opts.ticks.reverse ? scale.min : scale.max); + + // Point Label Font + var plFont = getPointLabelFontOptions(scale); + + ctx.textBaseline = 'top'; + + for (var i = getValueCount(scale) - 1; i >= 0; i--) { + if (angleLineOpts.display) { + var outerPosition = scale.getPointPosition(i, outerDistance); + ctx.beginPath(); + ctx.moveTo(scale.xCenter, scale.yCenter); + ctx.lineTo(outerPosition.x, outerPosition.y); + ctx.stroke(); + ctx.closePath(); + } + + if (pointLabelOpts.display) { + // Extra 3px out for some label spacing + var pointLabelPosition = scale.getPointPosition(i, outerDistance + 5); + + // Keep this in loop since we may support array properties here + var pointLabelFontColor = valueOrDefault(pointLabelOpts.fontColor, globalDefaults.defaultFontColor); + ctx.font = plFont.font; + ctx.fillStyle = pointLabelFontColor; + + var angleRadians = scale.getIndexAngle(i); + var angle = helpers.toDegrees(angleRadians); + ctx.textAlign = getTextAlignForAngle(angle); + adjustPointPositionForLabelHeight(angle, scale._pointLabelSizes[i], pointLabelPosition); + fillText(ctx, scale.pointLabels[i] || '', pointLabelPosition, plFont.size); + } + } + } + + function drawRadiusLine(scale, gridLineOpts, radius, index) { + var ctx = scale.ctx; + ctx.strokeStyle = helpers.valueAtIndexOrDefault(gridLineOpts.color, index - 1); + ctx.lineWidth = helpers.valueAtIndexOrDefault(gridLineOpts.lineWidth, index - 1); + + if (scale.options.gridLines.circular) { + // Draw circular arcs between the points + ctx.beginPath(); + ctx.arc(scale.xCenter, scale.yCenter, radius, 0, Math.PI * 2); + ctx.closePath(); + ctx.stroke(); + } else { + // Draw straight lines connecting each index + var valueCount = getValueCount(scale); + + if (valueCount === 0) { + return; + } + + ctx.beginPath(); + var pointPosition = scale.getPointPosition(0, radius); + ctx.moveTo(pointPosition.x, pointPosition.y); + + for (var i = 1; i < valueCount; i++) { + pointPosition = scale.getPointPosition(i, radius); + ctx.lineTo(pointPosition.x, pointPosition.y); + } + + ctx.closePath(); + ctx.stroke(); + } + } + + function numberOrZero(param) { + return helpers.isNumber(param) ? param : 0; + } + + var LinearRadialScale = Chart.LinearScaleBase.extend({ + setDimensions: function() { + var me = this; + var opts = me.options; + var tickOpts = opts.ticks; + // Set the unconstrained dimension before label rotation + me.width = me.maxWidth; + me.height = me.maxHeight; + me.xCenter = Math.round(me.width / 2); + me.yCenter = Math.round(me.height / 2); + + var minSize = helpers.min([me.height, me.width]); + var tickFontSize = helpers.valueOrDefault(tickOpts.fontSize, globalDefaults.defaultFontSize); + me.drawingArea = opts.display ? (minSize / 2) - (tickFontSize / 2 + tickOpts.backdropPaddingY) : (minSize / 2); + }, + determineDataLimits: function() { + var me = this; + var chart = me.chart; + var min = Number.POSITIVE_INFINITY; + var max = Number.NEGATIVE_INFINITY; + + helpers.each(chart.data.datasets, function(dataset, datasetIndex) { + if (chart.isDatasetVisible(datasetIndex)) { + var meta = chart.getDatasetMeta(datasetIndex); + + helpers.each(dataset.data, function(rawValue, index) { + var value = +me.getRightValue(rawValue); + if (isNaN(value) || meta.data[index].hidden) { + return; + } + + min = Math.min(value, min); + max = Math.max(value, max); + }); + } + }); + + me.min = (min === Number.POSITIVE_INFINITY ? 0 : min); + me.max = (max === Number.NEGATIVE_INFINITY ? 0 : max); + + // Common base implementation to handle ticks.min, ticks.max, ticks.beginAtZero + me.handleTickRangeOptions(); + }, + getTickLimit: function() { + var tickOpts = this.options.ticks; + var tickFontSize = helpers.valueOrDefault(tickOpts.fontSize, globalDefaults.defaultFontSize); + return Math.min(tickOpts.maxTicksLimit ? tickOpts.maxTicksLimit : 11, Math.ceil(this.drawingArea / (1.5 * tickFontSize))); + }, + convertTicksToLabels: function() { + var me = this; + + Chart.LinearScaleBase.prototype.convertTicksToLabels.call(me); + + // Point labels + me.pointLabels = me.chart.data.labels.map(me.options.pointLabels.callback, me); + }, + getLabelForIndex: function(index, datasetIndex) { + return +this.getRightValue(this.chart.data.datasets[datasetIndex].data[index]); + }, + fit: function() { + if (this.options.pointLabels.display) { + fitWithPointLabels(this); + } else { + fit(this); + } + }, + /** + * Set radius reductions and determine new radius and center point + * @private + */ + setReductions: function(largestPossibleRadius, furthestLimits, furthestAngles) { + var me = this; + var radiusReductionLeft = furthestLimits.l / Math.sin(furthestAngles.l); + var radiusReductionRight = Math.max(furthestLimits.r - me.width, 0) / Math.sin(furthestAngles.r); + var radiusReductionTop = -furthestLimits.t / Math.cos(furthestAngles.t); + var radiusReductionBottom = -Math.max(furthestLimits.b - me.height, 0) / Math.cos(furthestAngles.b); + + radiusReductionLeft = numberOrZero(radiusReductionLeft); + radiusReductionRight = numberOrZero(radiusReductionRight); + radiusReductionTop = numberOrZero(radiusReductionTop); + radiusReductionBottom = numberOrZero(radiusReductionBottom); + + me.drawingArea = Math.min( + Math.round(largestPossibleRadius - (radiusReductionLeft + radiusReductionRight) / 2), + Math.round(largestPossibleRadius - (radiusReductionTop + radiusReductionBottom) / 2)); + me.setCenterPoint(radiusReductionLeft, radiusReductionRight, radiusReductionTop, radiusReductionBottom); + }, + setCenterPoint: function(leftMovement, rightMovement, topMovement, bottomMovement) { + var me = this; + var maxRight = me.width - rightMovement - me.drawingArea; + var maxLeft = leftMovement + me.drawingArea; + var maxTop = topMovement + me.drawingArea; + var maxBottom = me.height - bottomMovement - me.drawingArea; + + me.xCenter = Math.round(((maxLeft + maxRight) / 2) + me.left); + me.yCenter = Math.round(((maxTop + maxBottom) / 2) + me.top); + }, + + getIndexAngle: function(index) { + var angleMultiplier = (Math.PI * 2) / getValueCount(this); + var startAngle = this.chart.options && this.chart.options.startAngle ? + this.chart.options.startAngle : + 0; + + var startAngleRadians = startAngle * Math.PI * 2 / 360; + + // Start from the top instead of right, so remove a quarter of the circle + return index * angleMultiplier + startAngleRadians; + }, + getDistanceFromCenterForValue: function(value) { + var me = this; + + if (value === null) { + return 0; // null always in center + } + + // Take into account half font size + the yPadding of the top value + var scalingFactor = me.drawingArea / (me.max - me.min); + if (me.options.ticks.reverse) { + return (me.max - value) * scalingFactor; + } + return (value - me.min) * scalingFactor; + }, + getPointPosition: function(index, distanceFromCenter) { + var me = this; + var thisAngle = me.getIndexAngle(index) - (Math.PI / 2); + return { + x: Math.round(Math.cos(thisAngle) * distanceFromCenter) + me.xCenter, + y: Math.round(Math.sin(thisAngle) * distanceFromCenter) + me.yCenter + }; + }, + getPointPositionForValue: function(index, value) { + return this.getPointPosition(index, this.getDistanceFromCenterForValue(value)); + }, + + getBasePosition: function() { + var me = this; + var min = me.min; + var max = me.max; + + return me.getPointPositionForValue(0, + me.beginAtZero ? 0 : + min < 0 && max < 0 ? max : + min > 0 && max > 0 ? min : + 0); + }, + + draw: function() { + var me = this; + var opts = me.options; + var gridLineOpts = opts.gridLines; + var tickOpts = opts.ticks; + var valueOrDefault = helpers.valueOrDefault; + + if (opts.display) { + var ctx = me.ctx; + var startAngle = this.getIndexAngle(0); + + // Tick Font + var tickFontSize = valueOrDefault(tickOpts.fontSize, globalDefaults.defaultFontSize); + var tickFontStyle = valueOrDefault(tickOpts.fontStyle, globalDefaults.defaultFontStyle); + var tickFontFamily = valueOrDefault(tickOpts.fontFamily, globalDefaults.defaultFontFamily); + var tickLabelFont = helpers.fontString(tickFontSize, tickFontStyle, tickFontFamily); + + helpers.each(me.ticks, function(label, index) { + // Don't draw a centre value (if it is minimum) + if (index > 0 || tickOpts.reverse) { + var yCenterOffset = me.getDistanceFromCenterForValue(me.ticksAsNumbers[index]); + + // Draw circular lines around the scale + if (gridLineOpts.display && index !== 0) { + drawRadiusLine(me, gridLineOpts, yCenterOffset, index); + } + + if (tickOpts.display) { + var tickFontColor = valueOrDefault(tickOpts.fontColor, globalDefaults.defaultFontColor); + ctx.font = tickLabelFont; + + ctx.save(); + ctx.translate(me.xCenter, me.yCenter); + ctx.rotate(startAngle); + + if (tickOpts.showLabelBackdrop) { + var labelWidth = ctx.measureText(label).width; + ctx.fillStyle = tickOpts.backdropColor; + ctx.fillRect( + -labelWidth / 2 - tickOpts.backdropPaddingX, + -yCenterOffset - tickFontSize / 2 - tickOpts.backdropPaddingY, + labelWidth + tickOpts.backdropPaddingX * 2, + tickFontSize + tickOpts.backdropPaddingY * 2 + ); + } + + ctx.textAlign = 'center'; + ctx.textBaseline = 'middle'; + ctx.fillStyle = tickFontColor; + ctx.fillText(label, 0, -yCenterOffset); + ctx.restore(); + } + } + }); + + if (opts.angleLines.display || opts.pointLabels.display) { + drawPointLabels(me); + } + } + } + }); + Chart.scaleService.registerScaleType('radialLinear', LinearRadialScale, defaultConfig); + +}; + +},{"25":25,"34":34,"45":45}],57:[function(require,module,exports){ +/* global window: false */ +'use strict'; + +var moment = require(1); +moment = typeof moment === 'function' ? moment : window.moment; + +var defaults = require(25); +var helpers = require(45); + +// Integer constants are from the ES6 spec. +var MIN_INTEGER = Number.MIN_SAFE_INTEGER || -9007199254740991; +var MAX_INTEGER = Number.MAX_SAFE_INTEGER || 9007199254740991; + +var INTERVALS = { + millisecond: { + common: true, + size: 1, + steps: [1, 2, 5, 10, 20, 50, 100, 250, 500] + }, + second: { + common: true, + size: 1000, + steps: [1, 2, 5, 10, 30] + }, + minute: { + common: true, + size: 60000, + steps: [1, 2, 5, 10, 30] + }, + hour: { + common: true, + size: 3600000, + steps: [1, 2, 3, 6, 12] + }, + day: { + common: true, + size: 86400000, + steps: [1, 2, 5] + }, + week: { + common: false, + size: 604800000, + steps: [1, 2, 3, 4] + }, + month: { + common: true, + size: 2.628e9, + steps: [1, 2, 3] + }, + quarter: { + common: false, + size: 7.884e9, + steps: [1, 2, 3, 4] + }, + year: { + common: true, + size: 3.154e10 + } +}; + +var UNITS = Object.keys(INTERVALS); + +function sorter(a, b) { + return a - b; +} + +function arrayUnique(items) { + var hash = {}; + var out = []; + var i, ilen, item; + + for (i = 0, ilen = items.length; i < ilen; ++i) { + item = items[i]; + if (!hash[item]) { + hash[item] = true; + out.push(item); + } + } + + return out; +} + +/** + * Returns an array of {time, pos} objects used to interpolate a specific `time` or position + * (`pos`) on the scale, by searching entries before and after the requested value. `pos` is + * a decimal between 0 and 1: 0 being the start of the scale (left or top) and 1 the other + * extremity (left + width or top + height). Note that it would be more optimized to directly + * store pre-computed pixels, but the scale dimensions are not guaranteed at the time we need + * to create the lookup table. The table ALWAYS contains at least two items: min and max. + * + * @param {Number[]} timestamps - timestamps sorted from lowest to highest. + * @param {String} distribution - If 'linear', timestamps will be spread linearly along the min + * and max range, so basically, the table will contains only two items: {min, 0} and {max, 1}. + * If 'series', timestamps will be positioned at the same distance from each other. In this + * case, only timestamps that break the time linearity are registered, meaning that in the + * best case, all timestamps are linear, the table contains only min and max. + */ +function buildLookupTable(timestamps, min, max, distribution) { + if (distribution === 'linear' || !timestamps.length) { + return [ + {time: min, pos: 0}, + {time: max, pos: 1} + ]; + } + + var table = []; + var items = [min]; + var i, ilen, prev, curr, next; + + for (i = 0, ilen = timestamps.length; i < ilen; ++i) { + curr = timestamps[i]; + if (curr > min && curr < max) { + items.push(curr); + } + } + + items.push(max); + + for (i = 0, ilen = items.length; i < ilen; ++i) { + next = items[i + 1]; + prev = items[i - 1]; + curr = items[i]; + + // only add points that breaks the scale linearity + if (prev === undefined || next === undefined || Math.round((next + prev) / 2) !== curr) { + table.push({time: curr, pos: i / (ilen - 1)}); + } + } + + return table; +} + +// @see adapted from http://www.anujgakhar.com/2014/03/01/binary-search-in-javascript/ +function lookup(table, key, value) { + var lo = 0; + var hi = table.length - 1; + var mid, i0, i1; + + while (lo >= 0 && lo <= hi) { + mid = (lo + hi) >> 1; + i0 = table[mid - 1] || null; + i1 = table[mid]; + + if (!i0) { + // given value is outside table (before first item) + return {lo: null, hi: i1}; + } else if (i1[key] < value) { + lo = mid + 1; + } else if (i0[key] > value) { + hi = mid - 1; + } else { + return {lo: i0, hi: i1}; + } + } + + // given value is outside table (after last item) + return {lo: i1, hi: null}; +} + +/** + * Linearly interpolates the given source `value` using the table items `skey` values and + * returns the associated `tkey` value. For example, interpolate(table, 'time', 42, 'pos') + * returns the position for a timestamp equal to 42. If value is out of bounds, values at + * index [0, 1] or [n - 1, n] are used for the interpolation. + */ +function interpolate(table, skey, sval, tkey) { + var range = lookup(table, skey, sval); + + // Note: the lookup table ALWAYS contains at least 2 items (min and max) + var prev = !range.lo ? table[0] : !range.hi ? table[table.length - 2] : range.lo; + var next = !range.lo ? table[1] : !range.hi ? table[table.length - 1] : range.hi; + + var span = next[skey] - prev[skey]; + var ratio = span ? (sval - prev[skey]) / span : 0; + var offset = (next[tkey] - prev[tkey]) * ratio; + + return prev[tkey] + offset; +} + +/** + * Convert the given value to a moment object using the given time options. + * @see http://momentjs.com/docs/#/parsing/ + */ +function momentify(value, options) { + var parser = options.parser; + var format = options.parser || options.format; + + if (typeof parser === 'function') { + return parser(value); + } + + if (typeof value === 'string' && typeof format === 'string') { + return moment(value, format); + } + + if (!(value instanceof moment)) { + value = moment(value); + } + + if (value.isValid()) { + return value; + } + + // Labels are in an incompatible moment format and no `parser` has been provided. + // The user might still use the deprecated `format` option to convert his inputs. + if (typeof format === 'function') { + return format(value); + } + + return value; +} + +function parse(input, scale) { + if (helpers.isNullOrUndef(input)) { + return null; + } + + var options = scale.options.time; + var value = momentify(scale.getRightValue(input), options); + if (!value.isValid()) { + return null; + } + + if (options.round) { + value.startOf(options.round); + } + + return value.valueOf(); +} + +/** + * Returns the number of unit to skip to be able to display up to `capacity` number of ticks + * in `unit` for the given `min` / `max` range and respecting the interval steps constraints. + */ +function determineStepSize(min, max, unit, capacity) { + var range = max - min; + var interval = INTERVALS[unit]; + var milliseconds = interval.size; + var steps = interval.steps; + var i, ilen, factor; + + if (!steps) { + return Math.ceil(range / ((capacity || 1) * milliseconds)); + } + + for (i = 0, ilen = steps.length; i < ilen; ++i) { + factor = steps[i]; + if (Math.ceil(range / (milliseconds * factor)) <= capacity) { + break; + } + } + + return factor; +} + +/** + * Figures out what unit results in an appropriate number of auto-generated ticks + */ +function determineUnitForAutoTicks(minUnit, min, max, capacity) { + var ilen = UNITS.length; + var i, interval, factor; + + for (i = UNITS.indexOf(minUnit); i < ilen - 1; ++i) { + interval = INTERVALS[UNITS[i]]; + factor = interval.steps ? interval.steps[interval.steps.length - 1] : MAX_INTEGER; + + if (interval.common && Math.ceil((max - min) / (factor * interval.size)) <= capacity) { + return UNITS[i]; + } + } + + return UNITS[ilen - 1]; +} + +/** + * Figures out what unit to format a set of ticks with + */ +function determineUnitForFormatting(ticks, minUnit, min, max) { + var duration = moment.duration(moment(max).diff(moment(min))); + var ilen = UNITS.length; + var i, unit; + + for (i = ilen - 1; i >= UNITS.indexOf(minUnit); i--) { + unit = UNITS[i]; + if (INTERVALS[unit].common && duration.as(unit) >= ticks.length) { + return unit; + } + } + + return UNITS[minUnit ? UNITS.indexOf(minUnit) : 0]; +} + +function determineMajorUnit(unit) { + for (var i = UNITS.indexOf(unit) + 1, ilen = UNITS.length; i < ilen; ++i) { + if (INTERVALS[UNITS[i]].common) { + return UNITS[i]; + } + } +} + +/** + * Generates a maximum of `capacity` timestamps between min and max, rounded to the + * `minor` unit, aligned on the `major` unit and using the given scale time `options`. + * Important: this method can return ticks outside the min and max range, it's the + * responsibility of the calling code to clamp values if needed. + */ +function generate(min, max, capacity, options) { + var timeOpts = options.time; + var minor = timeOpts.unit || determineUnitForAutoTicks(timeOpts.minUnit, min, max, capacity); + var major = determineMajorUnit(minor); + var stepSize = helpers.valueOrDefault(timeOpts.stepSize, timeOpts.unitStepSize); + var weekday = minor === 'week' ? timeOpts.isoWeekday : false; + var majorTicksEnabled = options.ticks.major.enabled; + var interval = INTERVALS[minor]; + var first = moment(min); + var last = moment(max); + var ticks = []; + var time; + + if (!stepSize) { + stepSize = determineStepSize(min, max, minor, capacity); + } + + // For 'week' unit, handle the first day of week option + if (weekday) { + first = first.isoWeekday(weekday); + last = last.isoWeekday(weekday); + } + + // Align first/last ticks on unit + first = first.startOf(weekday ? 'day' : minor); + last = last.startOf(weekday ? 'day' : minor); + + // Make sure that the last tick include max + if (last < max) { + last.add(1, minor); + } + + time = moment(first); + + if (majorTicksEnabled && major && !weekday && !timeOpts.round) { + // Align the first tick on the previous `minor` unit aligned on the `major` unit: + // we first aligned time on the previous `major` unit then add the number of full + // stepSize there is between first and the previous major time. + time.startOf(major); + time.add(~~((first - time) / (interval.size * stepSize)) * stepSize, minor); + } + + for (; time < last; time.add(stepSize, minor)) { + ticks.push(+time); + } + + ticks.push(+time); + + return ticks; +} + +/** + * Returns the right and left offsets from edges in the form of {left, right}. + * Offsets are added when the `offset` option is true. + */ +function computeOffsets(table, ticks, min, max, options) { + var left = 0; + var right = 0; + var upper, lower; + + if (options.offset && ticks.length) { + if (!options.time.min) { + upper = ticks.length > 1 ? ticks[1] : max; + lower = ticks[0]; + left = ( + interpolate(table, 'time', upper, 'pos') - + interpolate(table, 'time', lower, 'pos') + ) / 2; + } + if (!options.time.max) { + upper = ticks[ticks.length - 1]; + lower = ticks.length > 1 ? ticks[ticks.length - 2] : min; + right = ( + interpolate(table, 'time', upper, 'pos') - + interpolate(table, 'time', lower, 'pos') + ) / 2; + } + } + + return {left: left, right: right}; +} + +function ticksFromTimestamps(values, majorUnit) { + var ticks = []; + var i, ilen, value, major; + + for (i = 0, ilen = values.length; i < ilen; ++i) { + value = values[i]; + major = majorUnit ? value === +moment(value).startOf(majorUnit) : false; + + ticks.push({ + value: value, + major: major + }); + } + + return ticks; +} + +module.exports = function(Chart) { + + var defaultConfig = { + position: 'bottom', + + /** + * Data distribution along the scale: + * - 'linear': data are spread according to their time (distances can vary), + * - 'series': data are spread at the same distance from each other. + * @see https://github.com/chartjs/Chart.js/pull/4507 + * @since 2.7.0 + */ + distribution: 'linear', + + /** + * Scale boundary strategy (bypassed by min/max time options) + * - `data`: make sure data are fully visible, ticks outside are removed + * - `ticks`: make sure ticks are fully visible, data outside are truncated + * @see https://github.com/chartjs/Chart.js/pull/4556 + * @since 2.7.0 + */ + bounds: 'data', + + time: { + parser: false, // false == a pattern string from http://momentjs.com/docs/#/parsing/string-format/ or a custom callback that converts its argument to a moment + format: false, // DEPRECATED false == date objects, moment object, callback or a pattern string from http://momentjs.com/docs/#/parsing/string-format/ + unit: false, // false == automatic or override with week, month, year, etc. + round: false, // none, or override with week, month, year, etc. + displayFormat: false, // DEPRECATED + isoWeekday: false, // override week start day - see http://momentjs.com/docs/#/get-set/iso-weekday/ + minUnit: 'millisecond', + + // defaults to unit's corresponding unitFormat below or override using pattern string from http://momentjs.com/docs/#/displaying/format/ + displayFormats: { + millisecond: 'h:mm:ss.SSS a', // 11:20:01.123 AM, + second: 'h:mm:ss a', // 11:20:01 AM + minute: 'h:mm a', // 11:20 AM + hour: 'hA', // 5PM + day: 'MMM D', // Sep 4 + week: 'll', // Week 46, or maybe "[W]WW - YYYY" ? + month: 'MMM YYYY', // Sept 2015 + quarter: '[Q]Q - YYYY', // Q3 + year: 'YYYY' // 2015 + }, + }, + ticks: { + autoSkip: false, + + /** + * Ticks generation input values: + * - 'auto': generates "optimal" ticks based on scale size and time options. + * - 'data': generates ticks from data (including labels from data {t|x|y} objects). + * - 'labels': generates ticks from user given `data.labels` values ONLY. + * @see https://github.com/chartjs/Chart.js/pull/4507 + * @since 2.7.0 + */ + source: 'auto', + + major: { + enabled: false + } + } + }; + + var TimeScale = Chart.Scale.extend({ + initialize: function() { + if (!moment) { + throw new Error('Chart.js - Moment.js could not be found! You must include it before Chart.js to use the time scale. Download at https://momentjs.com'); + } + + this.mergeTicksOptions(); + + Chart.Scale.prototype.initialize.call(this); + }, + + update: function() { + var me = this; + var options = me.options; + + // DEPRECATIONS: output a message only one time per update + if (options.time && options.time.format) { + console.warn('options.time.format is deprecated and replaced by options.time.parser.'); + } + + return Chart.Scale.prototype.update.apply(me, arguments); + }, + + /** + * Allows data to be referenced via 't' attribute + */ + getRightValue: function(rawValue) { + if (rawValue && rawValue.t !== undefined) { + rawValue = rawValue.t; + } + return Chart.Scale.prototype.getRightValue.call(this, rawValue); + }, + + determineDataLimits: function() { + var me = this; + var chart = me.chart; + var timeOpts = me.options.time; + var min = MAX_INTEGER; + var max = MIN_INTEGER; + var timestamps = []; + var datasets = []; + var labels = []; + var i, j, ilen, jlen, data, timestamp; + + // Convert labels to timestamps + for (i = 0, ilen = chart.data.labels.length; i < ilen; ++i) { + labels.push(parse(chart.data.labels[i], me)); + } + + // Convert data to timestamps + for (i = 0, ilen = (chart.data.datasets || []).length; i < ilen; ++i) { + if (chart.isDatasetVisible(i)) { + data = chart.data.datasets[i].data; + + // Let's consider that all data have the same format. + if (helpers.isObject(data[0])) { + datasets[i] = []; + + for (j = 0, jlen = data.length; j < jlen; ++j) { + timestamp = parse(data[j], me); + timestamps.push(timestamp); + datasets[i][j] = timestamp; + } + } else { + timestamps.push.apply(timestamps, labels); + datasets[i] = labels.slice(0); + } + } else { + datasets[i] = []; + } + } + + if (labels.length) { + // Sort labels **after** data have been converted + labels = arrayUnique(labels).sort(sorter); + min = Math.min(min, labels[0]); + max = Math.max(max, labels[labels.length - 1]); + } + + if (timestamps.length) { + timestamps = arrayUnique(timestamps).sort(sorter); + min = Math.min(min, timestamps[0]); + max = Math.max(max, timestamps[timestamps.length - 1]); + } + + min = parse(timeOpts.min, me) || min; + max = parse(timeOpts.max, me) || max; + + // In case there is no valid min/max, let's use today limits + min = min === MAX_INTEGER ? +moment().startOf('day') : min; + max = max === MIN_INTEGER ? +moment().endOf('day') + 1 : max; + + // Make sure that max is strictly higher than min (required by the lookup table) + me.min = Math.min(min, max); + me.max = Math.max(min + 1, max); + + // PRIVATE + me._horizontal = me.isHorizontal(); + me._table = []; + me._timestamps = { + data: timestamps, + datasets: datasets, + labels: labels + }; + }, + + buildTicks: function() { + var me = this; + var min = me.min; + var max = me.max; + var options = me.options; + var timeOpts = options.time; + var timestamps = []; + var ticks = []; + var i, ilen, timestamp; + + switch (options.ticks.source) { + case 'data': + timestamps = me._timestamps.data; + break; + case 'labels': + timestamps = me._timestamps.labels; + break; + case 'auto': + default: + timestamps = generate(min, max, me.getLabelCapacity(min), options); + } + + if (options.bounds === 'ticks' && timestamps.length) { + min = timestamps[0]; + max = timestamps[timestamps.length - 1]; + } + + // Enforce limits with user min/max options + min = parse(timeOpts.min, me) || min; + max = parse(timeOpts.max, me) || max; + + // Remove ticks outside the min/max range + for (i = 0, ilen = timestamps.length; i < ilen; ++i) { + timestamp = timestamps[i]; + if (timestamp >= min && timestamp <= max) { + ticks.push(timestamp); + } + } + + me.min = min; + me.max = max; + + // PRIVATE + me._unit = timeOpts.unit || determineUnitForFormatting(ticks, timeOpts.minUnit, me.min, me.max); + me._majorUnit = determineMajorUnit(me._unit); + me._table = buildLookupTable(me._timestamps.data, min, max, options.distribution); + me._offsets = computeOffsets(me._table, ticks, min, max, options); + + return ticksFromTimestamps(ticks, me._majorUnit); + }, + + getLabelForIndex: function(index, datasetIndex) { + var me = this; + var data = me.chart.data; + var timeOpts = me.options.time; + var label = data.labels && index < data.labels.length ? data.labels[index] : ''; + var value = data.datasets[datasetIndex].data[index]; + + if (helpers.isObject(value)) { + label = me.getRightValue(value); + } + if (timeOpts.tooltipFormat) { + label = momentify(label, timeOpts).format(timeOpts.tooltipFormat); + } + + return label; + }, + + /** + * Function to format an individual tick mark + * @private + */ + tickFormatFunction: function(tick, index, ticks, formatOverride) { + var me = this; + var options = me.options; + var time = tick.valueOf(); + var formats = options.time.displayFormats; + var minorFormat = formats[me._unit]; + var majorUnit = me._majorUnit; + var majorFormat = formats[majorUnit]; + var majorTime = tick.clone().startOf(majorUnit).valueOf(); + var majorTickOpts = options.ticks.major; + var major = majorTickOpts.enabled && majorUnit && majorFormat && time === majorTime; + var label = tick.format(formatOverride ? formatOverride : major ? majorFormat : minorFormat); + var tickOpts = major ? majorTickOpts : options.ticks.minor; + var formatter = helpers.valueOrDefault(tickOpts.callback, tickOpts.userCallback); + + return formatter ? formatter(label, index, ticks) : label; + }, + + convertTicksToLabels: function(ticks) { + var labels = []; + var i, ilen; + + for (i = 0, ilen = ticks.length; i < ilen; ++i) { + labels.push(this.tickFormatFunction(moment(ticks[i].value), i, ticks)); + } + + return labels; + }, + + /** + * @private + */ + getPixelForOffset: function(time) { + var me = this; + var size = me._horizontal ? me.width : me.height; + var start = me._horizontal ? me.left : me.top; + var pos = interpolate(me._table, 'time', time, 'pos'); + + return start + size * (me._offsets.left + pos) / (me._offsets.left + 1 + me._offsets.right); + }, + + getPixelForValue: function(value, index, datasetIndex) { + var me = this; + var time = null; + + if (index !== undefined && datasetIndex !== undefined) { + time = me._timestamps.datasets[datasetIndex][index]; + } + + if (time === null) { + time = parse(value, me); + } + + if (time !== null) { + return me.getPixelForOffset(time); + } + }, + + getPixelForTick: function(index) { + var ticks = this.getTicks(); + return index >= 0 && index < ticks.length ? + this.getPixelForOffset(ticks[index].value) : + null; + }, + + getValueForPixel: function(pixel) { + var me = this; + var size = me._horizontal ? me.width : me.height; + var start = me._horizontal ? me.left : me.top; + var pos = (size ? (pixel - start) / size : 0) * (me._offsets.left + 1 + me._offsets.left) - me._offsets.right; + var time = interpolate(me._table, 'pos', pos, 'time'); + + return moment(time); + }, + + /** + * Crude approximation of what the label width might be + * @private + */ + getLabelWidth: function(label) { + var me = this; + var ticksOpts = me.options.ticks; + var tickLabelWidth = me.ctx.measureText(label).width; + var angle = helpers.toRadians(ticksOpts.maxRotation); + var cosRotation = Math.cos(angle); + var sinRotation = Math.sin(angle); + var tickFontSize = helpers.valueOrDefault(ticksOpts.fontSize, defaults.global.defaultFontSize); + + return (tickLabelWidth * cosRotation) + (tickFontSize * sinRotation); + }, + + /** + * @private + */ + getLabelCapacity: function(exampleTime) { + var me = this; + + var formatOverride = me.options.time.displayFormats.millisecond; // Pick the longest format for guestimation + + var exampleLabel = me.tickFormatFunction(moment(exampleTime), 0, [], formatOverride); + var tickLabelWidth = me.getLabelWidth(exampleLabel); + var innerWidth = me.isHorizontal() ? me.width : me.height; + + return Math.floor(innerWidth / tickLabelWidth); + } + }); + + Chart.scaleService.registerScaleType('time', TimeScale, defaultConfig); +}; + +},{"1":1,"25":25,"45":45}]},{},[7])(7) +}); \ No newline at end of file diff --git a/public/libs/uml/mermaid.js b/public/libs/uml/mermaid.js new file mode 100644 index 000000000..e004aeaa2 --- /dev/null +++ b/public/libs/uml/mermaid.js @@ -0,0 +1,65431 @@ +(function webpackUniversalModuleDefinition(root, factory) { + if(typeof exports === 'object' && typeof module === 'object') + module.exports = factory(); + else if(typeof define === 'function' && define.amd) + define([], factory); + else if(typeof exports === 'object') + exports["mermaid"] = factory(); + else + root["mermaid"] = factory(); +})(typeof self !== 'undefined' ? self : this, function() { +return /******/ (function(modules) { // webpackBootstrap +/******/ // The module cache +/******/ var installedModules = {}; +/******/ +/******/ // The require function +/******/ function __webpack_require__(moduleId) { +/******/ +/******/ // Check if module is in cache +/******/ if(installedModules[moduleId]) { +/******/ return installedModules[moduleId].exports; +/******/ } +/******/ // Create a new module (and put it into the cache) +/******/ var module = installedModules[moduleId] = { +/******/ i: moduleId, +/******/ l: false, +/******/ exports: {} +/******/ }; +/******/ +/******/ // Execute the module function +/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); +/******/ +/******/ // Flag the module as loaded +/******/ module.l = true; +/******/ +/******/ // Return the exports of the module +/******/ return module.exports; +/******/ } +/******/ +/******/ +/******/ // expose the modules object (__webpack_modules__) +/******/ __webpack_require__.m = modules; +/******/ +/******/ // expose the module cache +/******/ __webpack_require__.c = installedModules; +/******/ +/******/ // define getter function for harmony exports +/******/ __webpack_require__.d = function(exports, name, getter) { +/******/ if(!__webpack_require__.o(exports, name)) { +/******/ Object.defineProperty(exports, name, { +/******/ configurable: false, +/******/ enumerable: true, +/******/ get: getter +/******/ }); +/******/ } +/******/ }; +/******/ +/******/ // getDefaultExport function for compatibility with non-harmony modules +/******/ __webpack_require__.n = function(module) { +/******/ var getter = module && module.__esModule ? +/******/ function getDefault() { return module['default']; } : +/******/ function getModuleExports() { return module; }; +/******/ __webpack_require__.d(getter, 'a', getter); +/******/ return getter; +/******/ }; +/******/ +/******/ // Object.prototype.hasOwnProperty.call +/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; +/******/ +/******/ // __webpack_public_path__ +/******/ __webpack_require__.p = ""; +/******/ +/******/ // Load entry module and return exports +/******/ return __webpack_require__(__webpack_require__.s = 204); +/******/ }) +/************************************************************************/ +/******/ ([ +/* 0 */ +/***/ (function(module, exports, __webpack_require__) { + +/* WEBPACK VAR INJECTION */(function(module) {var require;//! moment.js +//! version : 2.20.1 +//! authors : Tim Wood, Iskren Chernev, Moment.js contributors +//! license : MIT +//! momentjs.com + +;(function (global, factory) { + true ? module.exports = factory() : + typeof define === 'function' && define.amd ? define(factory) : + global.moment = factory() +}(this, (function () { 'use strict'; + +var hookCallback; + +function hooks () { + return hookCallback.apply(null, arguments); +} + +// This is done to register the method called with moment() +// without creating circular dependencies. +function setHookCallback (callback) { + hookCallback = callback; +} + +function isArray(input) { + return input instanceof Array || Object.prototype.toString.call(input) === '[object Array]'; +} + +function isObject(input) { + // IE8 will treat undefined and null as object if it wasn't for + // input != null + return input != null && Object.prototype.toString.call(input) === '[object Object]'; +} + +function isObjectEmpty(obj) { + if (Object.getOwnPropertyNames) { + return (Object.getOwnPropertyNames(obj).length === 0); + } else { + var k; + for (k in obj) { + if (obj.hasOwnProperty(k)) { + return false; + } + } + return true; + } +} + +function isUndefined(input) { + return input === void 0; +} + +function isNumber(input) { + return typeof input === 'number' || Object.prototype.toString.call(input) === '[object Number]'; +} + +function isDate(input) { + return input instanceof Date || Object.prototype.toString.call(input) === '[object Date]'; +} + +function map(arr, fn) { + var res = [], i; + for (i = 0; i < arr.length; ++i) { + res.push(fn(arr[i], i)); + } + return res; +} + +function hasOwnProp(a, b) { + return Object.prototype.hasOwnProperty.call(a, b); +} + +function extend(a, b) { + for (var i in b) { + if (hasOwnProp(b, i)) { + a[i] = b[i]; + } + } + + if (hasOwnProp(b, 'toString')) { + a.toString = b.toString; + } + + if (hasOwnProp(b, 'valueOf')) { + a.valueOf = b.valueOf; + } + + return a; +} + +function createUTC (input, format, locale, strict) { + return createLocalOrUTC(input, format, locale, strict, true).utc(); +} + +function defaultParsingFlags() { + // We need to deep clone this object. + return { + empty : false, + unusedTokens : [], + unusedInput : [], + overflow : -2, + charsLeftOver : 0, + nullInput : false, + invalidMonth : null, + invalidFormat : false, + userInvalidated : false, + iso : false, + parsedDateParts : [], + meridiem : null, + rfc2822 : false, + weekdayMismatch : false + }; +} + +function getParsingFlags(m) { + if (m._pf == null) { + m._pf = defaultParsingFlags(); + } + return m._pf; +} + +var some; +if (Array.prototype.some) { + some = Array.prototype.some; +} else { + some = function (fun) { + var t = Object(this); + var len = t.length >>> 0; + + for (var i = 0; i < len; i++) { + if (i in t && fun.call(this, t[i], i, t)) { + return true; + } + } + + return false; + }; +} + +function isValid(m) { + if (m._isValid == null) { + var flags = getParsingFlags(m); + var parsedParts = some.call(flags.parsedDateParts, function (i) { + return i != null; + }); + var isNowValid = !isNaN(m._d.getTime()) && + flags.overflow < 0 && + !flags.empty && + !flags.invalidMonth && + !flags.invalidWeekday && + !flags.weekdayMismatch && + !flags.nullInput && + !flags.invalidFormat && + !flags.userInvalidated && + (!flags.meridiem || (flags.meridiem && parsedParts)); + + if (m._strict) { + isNowValid = isNowValid && + flags.charsLeftOver === 0 && + flags.unusedTokens.length === 0 && + flags.bigHour === undefined; + } + + if (Object.isFrozen == null || !Object.isFrozen(m)) { + m._isValid = isNowValid; + } + else { + return isNowValid; + } + } + return m._isValid; +} + +function createInvalid (flags) { + var m = createUTC(NaN); + if (flags != null) { + extend(getParsingFlags(m), flags); + } + else { + getParsingFlags(m).userInvalidated = true; + } + + return m; +} + +// Plugins that add properties should also add the key here (null value), +// so we can properly clone ourselves. +var momentProperties = hooks.momentProperties = []; + +function copyConfig(to, from) { + var i, prop, val; + + if (!isUndefined(from._isAMomentObject)) { + to._isAMomentObject = from._isAMomentObject; + } + if (!isUndefined(from._i)) { + to._i = from._i; + } + if (!isUndefined(from._f)) { + to._f = from._f; + } + if (!isUndefined(from._l)) { + to._l = from._l; + } + if (!isUndefined(from._strict)) { + to._strict = from._strict; + } + if (!isUndefined(from._tzm)) { + to._tzm = from._tzm; + } + if (!isUndefined(from._isUTC)) { + to._isUTC = from._isUTC; + } + if (!isUndefined(from._offset)) { + to._offset = from._offset; + } + if (!isUndefined(from._pf)) { + to._pf = getParsingFlags(from); + } + if (!isUndefined(from._locale)) { + to._locale = from._locale; + } + + if (momentProperties.length > 0) { + for (i = 0; i < momentProperties.length; i++) { + prop = momentProperties[i]; + val = from[prop]; + if (!isUndefined(val)) { + to[prop] = val; + } + } + } + + return to; +} + +var updateInProgress = false; + +// Moment prototype object +function Moment(config) { + copyConfig(this, config); + this._d = new Date(config._d != null ? config._d.getTime() : NaN); + if (!this.isValid()) { + this._d = new Date(NaN); + } + // Prevent infinite loop in case updateOffset creates new moment + // objects. + if (updateInProgress === false) { + updateInProgress = true; + hooks.updateOffset(this); + updateInProgress = false; + } +} + +function isMoment (obj) { + return obj instanceof Moment || (obj != null && obj._isAMomentObject != null); +} + +function absFloor (number) { + if (number < 0) { + // -0 -> 0 + return Math.ceil(number) || 0; + } else { + return Math.floor(number); + } +} + +function toInt(argumentForCoercion) { + var coercedNumber = +argumentForCoercion, + value = 0; + + if (coercedNumber !== 0 && isFinite(coercedNumber)) { + value = absFloor(coercedNumber); + } + + return value; +} + +// compare two arrays, return the number of differences +function compareArrays(array1, array2, dontConvert) { + var len = Math.min(array1.length, array2.length), + lengthDiff = Math.abs(array1.length - array2.length), + diffs = 0, + i; + for (i = 0; i < len; i++) { + if ((dontConvert && array1[i] !== array2[i]) || + (!dontConvert && toInt(array1[i]) !== toInt(array2[i]))) { + diffs++; + } + } + return diffs + lengthDiff; +} + +function warn(msg) { + if (hooks.suppressDeprecationWarnings === false && + (typeof console !== 'undefined') && console.warn) { + console.warn('Deprecation warning: ' + msg); + } +} + +function deprecate(msg, fn) { + var firstTime = true; + + return extend(function () { + if (hooks.deprecationHandler != null) { + hooks.deprecationHandler(null, msg); + } + if (firstTime) { + var args = []; + var arg; + for (var i = 0; i < arguments.length; i++) { + arg = ''; + if (typeof arguments[i] === 'object') { + arg += '\n[' + i + '] '; + for (var key in arguments[0]) { + arg += key + ': ' + arguments[0][key] + ', '; + } + arg = arg.slice(0, -2); // Remove trailing comma and space + } else { + arg = arguments[i]; + } + args.push(arg); + } + warn(msg + '\nArguments: ' + Array.prototype.slice.call(args).join('') + '\n' + (new Error()).stack); + firstTime = false; + } + return fn.apply(this, arguments); + }, fn); +} + +var deprecations = {}; + +function deprecateSimple(name, msg) { + if (hooks.deprecationHandler != null) { + hooks.deprecationHandler(name, msg); + } + if (!deprecations[name]) { + warn(msg); + deprecations[name] = true; + } +} + +hooks.suppressDeprecationWarnings = false; +hooks.deprecationHandler = null; + +function isFunction(input) { + return input instanceof Function || Object.prototype.toString.call(input) === '[object Function]'; +} + +function set (config) { + var prop, i; + for (i in config) { + prop = config[i]; + if (isFunction(prop)) { + this[i] = prop; + } else { + this['_' + i] = prop; + } + } + this._config = config; + // Lenient ordinal parsing accepts just a number in addition to + // number + (possibly) stuff coming from _dayOfMonthOrdinalParse. + // TODO: Remove "ordinalParse" fallback in next major release. + this._dayOfMonthOrdinalParseLenient = new RegExp( + (this._dayOfMonthOrdinalParse.source || this._ordinalParse.source) + + '|' + (/\d{1,2}/).source); +} + +function mergeConfigs(parentConfig, childConfig) { + var res = extend({}, parentConfig), prop; + for (prop in childConfig) { + if (hasOwnProp(childConfig, prop)) { + if (isObject(parentConfig[prop]) && isObject(childConfig[prop])) { + res[prop] = {}; + extend(res[prop], parentConfig[prop]); + extend(res[prop], childConfig[prop]); + } else if (childConfig[prop] != null) { + res[prop] = childConfig[prop]; + } else { + delete res[prop]; + } + } + } + for (prop in parentConfig) { + if (hasOwnProp(parentConfig, prop) && + !hasOwnProp(childConfig, prop) && + isObject(parentConfig[prop])) { + // make sure changes to properties don't modify parent config + res[prop] = extend({}, res[prop]); + } + } + return res; +} + +function Locale(config) { + if (config != null) { + this.set(config); + } +} + +var keys; + +if (Object.keys) { + keys = Object.keys; +} else { + keys = function (obj) { + var i, res = []; + for (i in obj) { + if (hasOwnProp(obj, i)) { + res.push(i); + } + } + return res; + }; +} + +var defaultCalendar = { + sameDay : '[Today at] LT', + nextDay : '[Tomorrow at] LT', + nextWeek : 'dddd [at] LT', + lastDay : '[Yesterday at] LT', + lastWeek : '[Last] dddd [at] LT', + sameElse : 'L' +}; + +function calendar (key, mom, now) { + var output = this._calendar[key] || this._calendar['sameElse']; + return isFunction(output) ? output.call(mom, now) : output; +} + +var defaultLongDateFormat = { + LTS : 'h:mm:ss A', + LT : 'h:mm A', + L : 'MM/DD/YYYY', + LL : 'MMMM D, YYYY', + LLL : 'MMMM D, YYYY h:mm A', + LLLL : 'dddd, MMMM D, YYYY h:mm A' +}; + +function longDateFormat (key) { + var format = this._longDateFormat[key], + formatUpper = this._longDateFormat[key.toUpperCase()]; + + if (format || !formatUpper) { + return format; + } + + this._longDateFormat[key] = formatUpper.replace(/MMMM|MM|DD|dddd/g, function (val) { + return val.slice(1); + }); + + return this._longDateFormat[key]; +} + +var defaultInvalidDate = 'Invalid date'; + +function invalidDate () { + return this._invalidDate; +} + +var defaultOrdinal = '%d'; +var defaultDayOfMonthOrdinalParse = /\d{1,2}/; + +function ordinal (number) { + return this._ordinal.replace('%d', number); +} + +var defaultRelativeTime = { + future : 'in %s', + past : '%s ago', + s : 'a few seconds', + ss : '%d seconds', + m : 'a minute', + mm : '%d minutes', + h : 'an hour', + hh : '%d hours', + d : 'a day', + dd : '%d days', + M : 'a month', + MM : '%d months', + y : 'a year', + yy : '%d years' +}; + +function relativeTime (number, withoutSuffix, string, isFuture) { + var output = this._relativeTime[string]; + return (isFunction(output)) ? + output(number, withoutSuffix, string, isFuture) : + output.replace(/%d/i, number); +} + +function pastFuture (diff, output) { + var format = this._relativeTime[diff > 0 ? 'future' : 'past']; + return isFunction(format) ? format(output) : format.replace(/%s/i, output); +} + +var aliases = {}; + +function addUnitAlias (unit, shorthand) { + var lowerCase = unit.toLowerCase(); + aliases[lowerCase] = aliases[lowerCase + 's'] = aliases[shorthand] = unit; +} + +function normalizeUnits(units) { + return typeof units === 'string' ? aliases[units] || aliases[units.toLowerCase()] : undefined; +} + +function normalizeObjectUnits(inputObject) { + var normalizedInput = {}, + normalizedProp, + prop; + + for (prop in inputObject) { + if (hasOwnProp(inputObject, prop)) { + normalizedProp = normalizeUnits(prop); + if (normalizedProp) { + normalizedInput[normalizedProp] = inputObject[prop]; + } + } + } + + return normalizedInput; +} + +var priorities = {}; + +function addUnitPriority(unit, priority) { + priorities[unit] = priority; +} + +function getPrioritizedUnits(unitsObj) { + var units = []; + for (var u in unitsObj) { + units.push({unit: u, priority: priorities[u]}); + } + units.sort(function (a, b) { + return a.priority - b.priority; + }); + return units; +} + +function zeroFill(number, targetLength, forceSign) { + var absNumber = '' + Math.abs(number), + zerosToFill = targetLength - absNumber.length, + sign = number >= 0; + return (sign ? (forceSign ? '+' : '') : '-') + + Math.pow(10, Math.max(0, zerosToFill)).toString().substr(1) + absNumber; +} + +var formattingTokens = /(\[[^\[]*\])|(\\)?([Hh]mm(ss)?|Mo|MM?M?M?|Do|DDDo|DD?D?D?|ddd?d?|do?|w[o|w]?|W[o|W]?|Qo?|YYYYYY|YYYYY|YYYY|YY|gg(ggg?)?|GG(GGG?)?|e|E|a|A|hh?|HH?|kk?|mm?|ss?|S{1,9}|x|X|zz?|ZZ?|.)/g; + +var localFormattingTokens = /(\[[^\[]*\])|(\\)?(LTS|LT|LL?L?L?|l{1,4})/g; + +var formatFunctions = {}; + +var formatTokenFunctions = {}; + +// token: 'M' +// padded: ['MM', 2] +// ordinal: 'Mo' +// callback: function () { this.month() + 1 } +function addFormatToken (token, padded, ordinal, callback) { + var func = callback; + if (typeof callback === 'string') { + func = function () { + return this[callback](); + }; + } + if (token) { + formatTokenFunctions[token] = func; + } + if (padded) { + formatTokenFunctions[padded[0]] = function () { + return zeroFill(func.apply(this, arguments), padded[1], padded[2]); + }; + } + if (ordinal) { + formatTokenFunctions[ordinal] = function () { + return this.localeData().ordinal(func.apply(this, arguments), token); + }; + } +} + +function removeFormattingTokens(input) { + if (input.match(/\[[\s\S]/)) { + return input.replace(/^\[|\]$/g, ''); + } + return input.replace(/\\/g, ''); +} + +function makeFormatFunction(format) { + var array = format.match(formattingTokens), i, length; + + for (i = 0, length = array.length; i < length; i++) { + if (formatTokenFunctions[array[i]]) { + array[i] = formatTokenFunctions[array[i]]; + } else { + array[i] = removeFormattingTokens(array[i]); + } + } + + return function (mom) { + var output = '', i; + for (i = 0; i < length; i++) { + output += isFunction(array[i]) ? array[i].call(mom, format) : array[i]; + } + return output; + }; +} + +// format date using native date object +function formatMoment(m, format) { + if (!m.isValid()) { + return m.localeData().invalidDate(); + } + + format = expandFormat(format, m.localeData()); + formatFunctions[format] = formatFunctions[format] || makeFormatFunction(format); + + return formatFunctions[format](m); +} + +function expandFormat(format, locale) { + var i = 5; + + function replaceLongDateFormatTokens(input) { + return locale.longDateFormat(input) || input; + } + + localFormattingTokens.lastIndex = 0; + while (i >= 0 && localFormattingTokens.test(format)) { + format = format.replace(localFormattingTokens, replaceLongDateFormatTokens); + localFormattingTokens.lastIndex = 0; + i -= 1; + } + + return format; +} + +var match1 = /\d/; // 0 - 9 +var match2 = /\d\d/; // 00 - 99 +var match3 = /\d{3}/; // 000 - 999 +var match4 = /\d{4}/; // 0000 - 9999 +var match6 = /[+-]?\d{6}/; // -999999 - 999999 +var match1to2 = /\d\d?/; // 0 - 99 +var match3to4 = /\d\d\d\d?/; // 999 - 9999 +var match5to6 = /\d\d\d\d\d\d?/; // 99999 - 999999 +var match1to3 = /\d{1,3}/; // 0 - 999 +var match1to4 = /\d{1,4}/; // 0 - 9999 +var match1to6 = /[+-]?\d{1,6}/; // -999999 - 999999 + +var matchUnsigned = /\d+/; // 0 - inf +var matchSigned = /[+-]?\d+/; // -inf - inf + +var matchOffset = /Z|[+-]\d\d:?\d\d/gi; // +00:00 -00:00 +0000 -0000 or Z +var matchShortOffset = /Z|[+-]\d\d(?::?\d\d)?/gi; // +00 -00 +00:00 -00:00 +0000 -0000 or Z + +var matchTimestamp = /[+-]?\d+(\.\d{1,3})?/; // 123456789 123456789.123 + +// any word (or two) characters or numbers including two/three word month in arabic. +// includes scottish gaelic two word and hyphenated months +var matchWord = /[0-9]{0,256}['a-z\u00A0-\u05FF\u0700-\uD7FF\uF900-\uFDCF\uFDF0-\uFF07\uFF10-\uFFEF]{1,256}|[\u0600-\u06FF\/]{1,256}(\s*?[\u0600-\u06FF]{1,256}){1,2}/i; + + +var regexes = {}; + +function addRegexToken (token, regex, strictRegex) { + regexes[token] = isFunction(regex) ? regex : function (isStrict, localeData) { + return (isStrict && strictRegex) ? strictRegex : regex; + }; +} + +function getParseRegexForToken (token, config) { + if (!hasOwnProp(regexes, token)) { + return new RegExp(unescapeFormat(token)); + } + + return regexes[token](config._strict, config._locale); +} + +// Code from http://stackoverflow.com/questions/3561493/is-there-a-regexp-escape-function-in-javascript +function unescapeFormat(s) { + return regexEscape(s.replace('\\', '').replace(/\\(\[)|\\(\])|\[([^\]\[]*)\]|\\(.)/g, function (matched, p1, p2, p3, p4) { + return p1 || p2 || p3 || p4; + })); +} + +function regexEscape(s) { + return s.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&'); +} + +var tokens = {}; + +function addParseToken (token, callback) { + var i, func = callback; + if (typeof token === 'string') { + token = [token]; + } + if (isNumber(callback)) { + func = function (input, array) { + array[callback] = toInt(input); + }; + } + for (i = 0; i < token.length; i++) { + tokens[token[i]] = func; + } +} + +function addWeekParseToken (token, callback) { + addParseToken(token, function (input, array, config, token) { + config._w = config._w || {}; + callback(input, config._w, config, token); + }); +} + +function addTimeToArrayFromToken(token, input, config) { + if (input != null && hasOwnProp(tokens, token)) { + tokens[token](input, config._a, config, token); + } +} + +var YEAR = 0; +var MONTH = 1; +var DATE = 2; +var HOUR = 3; +var MINUTE = 4; +var SECOND = 5; +var MILLISECOND = 6; +var WEEK = 7; +var WEEKDAY = 8; + +// FORMATTING + +addFormatToken('Y', 0, 0, function () { + var y = this.year(); + return y <= 9999 ? '' + y : '+' + y; +}); + +addFormatToken(0, ['YY', 2], 0, function () { + return this.year() % 100; +}); + +addFormatToken(0, ['YYYY', 4], 0, 'year'); +addFormatToken(0, ['YYYYY', 5], 0, 'year'); +addFormatToken(0, ['YYYYYY', 6, true], 0, 'year'); + +// ALIASES + +addUnitAlias('year', 'y'); + +// PRIORITIES + +addUnitPriority('year', 1); + +// PARSING + +addRegexToken('Y', matchSigned); +addRegexToken('YY', match1to2, match2); +addRegexToken('YYYY', match1to4, match4); +addRegexToken('YYYYY', match1to6, match6); +addRegexToken('YYYYYY', match1to6, match6); + +addParseToken(['YYYYY', 'YYYYYY'], YEAR); +addParseToken('YYYY', function (input, array) { + array[YEAR] = input.length === 2 ? hooks.parseTwoDigitYear(input) : toInt(input); +}); +addParseToken('YY', function (input, array) { + array[YEAR] = hooks.parseTwoDigitYear(input); +}); +addParseToken('Y', function (input, array) { + array[YEAR] = parseInt(input, 10); +}); + +// HELPERS + +function daysInYear(year) { + return isLeapYear(year) ? 366 : 365; +} + +function isLeapYear(year) { + return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0; +} + +// HOOKS + +hooks.parseTwoDigitYear = function (input) { + return toInt(input) + (toInt(input) > 68 ? 1900 : 2000); +}; + +// MOMENTS + +var getSetYear = makeGetSet('FullYear', true); + +function getIsLeapYear () { + return isLeapYear(this.year()); +} + +function makeGetSet (unit, keepTime) { + return function (value) { + if (value != null) { + set$1(this, unit, value); + hooks.updateOffset(this, keepTime); + return this; + } else { + return get(this, unit); + } + }; +} + +function get (mom, unit) { + return mom.isValid() ? + mom._d['get' + (mom._isUTC ? 'UTC' : '') + unit]() : NaN; +} + +function set$1 (mom, unit, value) { + if (mom.isValid() && !isNaN(value)) { + if (unit === 'FullYear' && isLeapYear(mom.year()) && mom.month() === 1 && mom.date() === 29) { + mom._d['set' + (mom._isUTC ? 'UTC' : '') + unit](value, mom.month(), daysInMonth(value, mom.month())); + } + else { + mom._d['set' + (mom._isUTC ? 'UTC' : '') + unit](value); + } + } +} + +// MOMENTS + +function stringGet (units) { + units = normalizeUnits(units); + if (isFunction(this[units])) { + return this[units](); + } + return this; +} + + +function stringSet (units, value) { + if (typeof units === 'object') { + units = normalizeObjectUnits(units); + var prioritized = getPrioritizedUnits(units); + for (var i = 0; i < prioritized.length; i++) { + this[prioritized[i].unit](units[prioritized[i].unit]); + } + } else { + units = normalizeUnits(units); + if (isFunction(this[units])) { + return this[units](value); + } + } + return this; +} + +function mod(n, x) { + return ((n % x) + x) % x; +} + +var indexOf; + +if (Array.prototype.indexOf) { + indexOf = Array.prototype.indexOf; +} else { + indexOf = function (o) { + // I know + var i; + for (i = 0; i < this.length; ++i) { + if (this[i] === o) { + return i; + } + } + return -1; + }; +} + +function daysInMonth(year, month) { + if (isNaN(year) || isNaN(month)) { + return NaN; + } + var modMonth = mod(month, 12); + year += (month - modMonth) / 12; + return modMonth === 1 ? (isLeapYear(year) ? 29 : 28) : (31 - modMonth % 7 % 2); +} + +// FORMATTING + +addFormatToken('M', ['MM', 2], 'Mo', function () { + return this.month() + 1; +}); + +addFormatToken('MMM', 0, 0, function (format) { + return this.localeData().monthsShort(this, format); +}); + +addFormatToken('MMMM', 0, 0, function (format) { + return this.localeData().months(this, format); +}); + +// ALIASES + +addUnitAlias('month', 'M'); + +// PRIORITY + +addUnitPriority('month', 8); + +// PARSING + +addRegexToken('M', match1to2); +addRegexToken('MM', match1to2, match2); +addRegexToken('MMM', function (isStrict, locale) { + return locale.monthsShortRegex(isStrict); +}); +addRegexToken('MMMM', function (isStrict, locale) { + return locale.monthsRegex(isStrict); +}); + +addParseToken(['M', 'MM'], function (input, array) { + array[MONTH] = toInt(input) - 1; +}); + +addParseToken(['MMM', 'MMMM'], function (input, array, config, token) { + var month = config._locale.monthsParse(input, token, config._strict); + // if we didn't find a month name, mark the date as invalid. + if (month != null) { + array[MONTH] = month; + } else { + getParsingFlags(config).invalidMonth = input; + } +}); + +// LOCALES + +var MONTHS_IN_FORMAT = /D[oD]?(\[[^\[\]]*\]|\s)+MMMM?/; +var defaultLocaleMonths = 'January_February_March_April_May_June_July_August_September_October_November_December'.split('_'); +function localeMonths (m, format) { + if (!m) { + return isArray(this._months) ? this._months : + this._months['standalone']; + } + return isArray(this._months) ? this._months[m.month()] : + this._months[(this._months.isFormat || MONTHS_IN_FORMAT).test(format) ? 'format' : 'standalone'][m.month()]; +} + +var defaultLocaleMonthsShort = 'Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec'.split('_'); +function localeMonthsShort (m, format) { + if (!m) { + return isArray(this._monthsShort) ? this._monthsShort : + this._monthsShort['standalone']; + } + return isArray(this._monthsShort) ? this._monthsShort[m.month()] : + this._monthsShort[MONTHS_IN_FORMAT.test(format) ? 'format' : 'standalone'][m.month()]; +} + +function handleStrictParse(monthName, format, strict) { + var i, ii, mom, llc = monthName.toLocaleLowerCase(); + if (!this._monthsParse) { + // this is not used + this._monthsParse = []; + this._longMonthsParse = []; + this._shortMonthsParse = []; + for (i = 0; i < 12; ++i) { + mom = createUTC([2000, i]); + this._shortMonthsParse[i] = this.monthsShort(mom, '').toLocaleLowerCase(); + this._longMonthsParse[i] = this.months(mom, '').toLocaleLowerCase(); + } + } + + if (strict) { + if (format === 'MMM') { + ii = indexOf.call(this._shortMonthsParse, llc); + return ii !== -1 ? ii : null; + } else { + ii = indexOf.call(this._longMonthsParse, llc); + return ii !== -1 ? ii : null; + } + } else { + if (format === 'MMM') { + ii = indexOf.call(this._shortMonthsParse, llc); + if (ii !== -1) { + return ii; + } + ii = indexOf.call(this._longMonthsParse, llc); + return ii !== -1 ? ii : null; + } else { + ii = indexOf.call(this._longMonthsParse, llc); + if (ii !== -1) { + return ii; + } + ii = indexOf.call(this._shortMonthsParse, llc); + return ii !== -1 ? ii : null; + } + } +} + +function localeMonthsParse (monthName, format, strict) { + var i, mom, regex; + + if (this._monthsParseExact) { + return handleStrictParse.call(this, monthName, format, strict); + } + + if (!this._monthsParse) { + this._monthsParse = []; + this._longMonthsParse = []; + this._shortMonthsParse = []; + } + + // TODO: add sorting + // Sorting makes sure if one month (or abbr) is a prefix of another + // see sorting in computeMonthsParse + for (i = 0; i < 12; i++) { + // make the regex if we don't have it already + mom = createUTC([2000, i]); + if (strict && !this._longMonthsParse[i]) { + this._longMonthsParse[i] = new RegExp('^' + this.months(mom, '').replace('.', '') + '$', 'i'); + this._shortMonthsParse[i] = new RegExp('^' + this.monthsShort(mom, '').replace('.', '') + '$', 'i'); + } + if (!strict && !this._monthsParse[i]) { + regex = '^' + this.months(mom, '') + '|^' + this.monthsShort(mom, ''); + this._monthsParse[i] = new RegExp(regex.replace('.', ''), 'i'); + } + // test the regex + if (strict && format === 'MMMM' && this._longMonthsParse[i].test(monthName)) { + return i; + } else if (strict && format === 'MMM' && this._shortMonthsParse[i].test(monthName)) { + return i; + } else if (!strict && this._monthsParse[i].test(monthName)) { + return i; + } + } +} + +// MOMENTS + +function setMonth (mom, value) { + var dayOfMonth; + + if (!mom.isValid()) { + // No op + return mom; + } + + if (typeof value === 'string') { + if (/^\d+$/.test(value)) { + value = toInt(value); + } else { + value = mom.localeData().monthsParse(value); + // TODO: Another silent failure? + if (!isNumber(value)) { + return mom; + } + } + } + + dayOfMonth = Math.min(mom.date(), daysInMonth(mom.year(), value)); + mom._d['set' + (mom._isUTC ? 'UTC' : '') + 'Month'](value, dayOfMonth); + return mom; +} + +function getSetMonth (value) { + if (value != null) { + setMonth(this, value); + hooks.updateOffset(this, true); + return this; + } else { + return get(this, 'Month'); + } +} + +function getDaysInMonth () { + return daysInMonth(this.year(), this.month()); +} + +var defaultMonthsShortRegex = matchWord; +function monthsShortRegex (isStrict) { + if (this._monthsParseExact) { + if (!hasOwnProp(this, '_monthsRegex')) { + computeMonthsParse.call(this); + } + if (isStrict) { + return this._monthsShortStrictRegex; + } else { + return this._monthsShortRegex; + } + } else { + if (!hasOwnProp(this, '_monthsShortRegex')) { + this._monthsShortRegex = defaultMonthsShortRegex; + } + return this._monthsShortStrictRegex && isStrict ? + this._monthsShortStrictRegex : this._monthsShortRegex; + } +} + +var defaultMonthsRegex = matchWord; +function monthsRegex (isStrict) { + if (this._monthsParseExact) { + if (!hasOwnProp(this, '_monthsRegex')) { + computeMonthsParse.call(this); + } + if (isStrict) { + return this._monthsStrictRegex; + } else { + return this._monthsRegex; + } + } else { + if (!hasOwnProp(this, '_monthsRegex')) { + this._monthsRegex = defaultMonthsRegex; + } + return this._monthsStrictRegex && isStrict ? + this._monthsStrictRegex : this._monthsRegex; + } +} + +function computeMonthsParse () { + function cmpLenRev(a, b) { + return b.length - a.length; + } + + var shortPieces = [], longPieces = [], mixedPieces = [], + i, mom; + for (i = 0; i < 12; i++) { + // make the regex if we don't have it already + mom = createUTC([2000, i]); + shortPieces.push(this.monthsShort(mom, '')); + longPieces.push(this.months(mom, '')); + mixedPieces.push(this.months(mom, '')); + mixedPieces.push(this.monthsShort(mom, '')); + } + // Sorting makes sure if one month (or abbr) is a prefix of another it + // will match the longer piece. + shortPieces.sort(cmpLenRev); + longPieces.sort(cmpLenRev); + mixedPieces.sort(cmpLenRev); + for (i = 0; i < 12; i++) { + shortPieces[i] = regexEscape(shortPieces[i]); + longPieces[i] = regexEscape(longPieces[i]); + } + for (i = 0; i < 24; i++) { + mixedPieces[i] = regexEscape(mixedPieces[i]); + } + + this._monthsRegex = new RegExp('^(' + mixedPieces.join('|') + ')', 'i'); + this._monthsShortRegex = this._monthsRegex; + this._monthsStrictRegex = new RegExp('^(' + longPieces.join('|') + ')', 'i'); + this._monthsShortStrictRegex = new RegExp('^(' + shortPieces.join('|') + ')', 'i'); +} + +function createDate (y, m, d, h, M, s, ms) { + // can't just apply() to create a date: + // https://stackoverflow.com/q/181348 + var date = new Date(y, m, d, h, M, s, ms); + + // the date constructor remaps years 0-99 to 1900-1999 + if (y < 100 && y >= 0 && isFinite(date.getFullYear())) { + date.setFullYear(y); + } + return date; +} + +function createUTCDate (y) { + var date = new Date(Date.UTC.apply(null, arguments)); + + // the Date.UTC function remaps years 0-99 to 1900-1999 + if (y < 100 && y >= 0 && isFinite(date.getUTCFullYear())) { + date.setUTCFullYear(y); + } + return date; +} + +// start-of-first-week - start-of-year +function firstWeekOffset(year, dow, doy) { + var // first-week day -- which january is always in the first week (4 for iso, 1 for other) + fwd = 7 + dow - doy, + // first-week day local weekday -- which local weekday is fwd + fwdlw = (7 + createUTCDate(year, 0, fwd).getUTCDay() - dow) % 7; + + return -fwdlw + fwd - 1; +} + +// https://en.wikipedia.org/wiki/ISO_week_date#Calculating_a_date_given_the_year.2C_week_number_and_weekday +function dayOfYearFromWeeks(year, week, weekday, dow, doy) { + var localWeekday = (7 + weekday - dow) % 7, + weekOffset = firstWeekOffset(year, dow, doy), + dayOfYear = 1 + 7 * (week - 1) + localWeekday + weekOffset, + resYear, resDayOfYear; + + if (dayOfYear <= 0) { + resYear = year - 1; + resDayOfYear = daysInYear(resYear) + dayOfYear; + } else if (dayOfYear > daysInYear(year)) { + resYear = year + 1; + resDayOfYear = dayOfYear - daysInYear(year); + } else { + resYear = year; + resDayOfYear = dayOfYear; + } + + return { + year: resYear, + dayOfYear: resDayOfYear + }; +} + +function weekOfYear(mom, dow, doy) { + var weekOffset = firstWeekOffset(mom.year(), dow, doy), + week = Math.floor((mom.dayOfYear() - weekOffset - 1) / 7) + 1, + resWeek, resYear; + + if (week < 1) { + resYear = mom.year() - 1; + resWeek = week + weeksInYear(resYear, dow, doy); + } else if (week > weeksInYear(mom.year(), dow, doy)) { + resWeek = week - weeksInYear(mom.year(), dow, doy); + resYear = mom.year() + 1; + } else { + resYear = mom.year(); + resWeek = week; + } + + return { + week: resWeek, + year: resYear + }; +} + +function weeksInYear(year, dow, doy) { + var weekOffset = firstWeekOffset(year, dow, doy), + weekOffsetNext = firstWeekOffset(year + 1, dow, doy); + return (daysInYear(year) - weekOffset + weekOffsetNext) / 7; +} + +// FORMATTING + +addFormatToken('w', ['ww', 2], 'wo', 'week'); +addFormatToken('W', ['WW', 2], 'Wo', 'isoWeek'); + +// ALIASES + +addUnitAlias('week', 'w'); +addUnitAlias('isoWeek', 'W'); + +// PRIORITIES + +addUnitPriority('week', 5); +addUnitPriority('isoWeek', 5); + +// PARSING + +addRegexToken('w', match1to2); +addRegexToken('ww', match1to2, match2); +addRegexToken('W', match1to2); +addRegexToken('WW', match1to2, match2); + +addWeekParseToken(['w', 'ww', 'W', 'WW'], function (input, week, config, token) { + week[token.substr(0, 1)] = toInt(input); +}); + +// HELPERS + +// LOCALES + +function localeWeek (mom) { + return weekOfYear(mom, this._week.dow, this._week.doy).week; +} + +var defaultLocaleWeek = { + dow : 0, // Sunday is the first day of the week. + doy : 6 // The week that contains Jan 1st is the first week of the year. +}; + +function localeFirstDayOfWeek () { + return this._week.dow; +} + +function localeFirstDayOfYear () { + return this._week.doy; +} + +// MOMENTS + +function getSetWeek (input) { + var week = this.localeData().week(this); + return input == null ? week : this.add((input - week) * 7, 'd'); +} + +function getSetISOWeek (input) { + var week = weekOfYear(this, 1, 4).week; + return input == null ? week : this.add((input - week) * 7, 'd'); +} + +// FORMATTING + +addFormatToken('d', 0, 'do', 'day'); + +addFormatToken('dd', 0, 0, function (format) { + return this.localeData().weekdaysMin(this, format); +}); + +addFormatToken('ddd', 0, 0, function (format) { + return this.localeData().weekdaysShort(this, format); +}); + +addFormatToken('dddd', 0, 0, function (format) { + return this.localeData().weekdays(this, format); +}); + +addFormatToken('e', 0, 0, 'weekday'); +addFormatToken('E', 0, 0, 'isoWeekday'); + +// ALIASES + +addUnitAlias('day', 'd'); +addUnitAlias('weekday', 'e'); +addUnitAlias('isoWeekday', 'E'); + +// PRIORITY +addUnitPriority('day', 11); +addUnitPriority('weekday', 11); +addUnitPriority('isoWeekday', 11); + +// PARSING + +addRegexToken('d', match1to2); +addRegexToken('e', match1to2); +addRegexToken('E', match1to2); +addRegexToken('dd', function (isStrict, locale) { + return locale.weekdaysMinRegex(isStrict); +}); +addRegexToken('ddd', function (isStrict, locale) { + return locale.weekdaysShortRegex(isStrict); +}); +addRegexToken('dddd', function (isStrict, locale) { + return locale.weekdaysRegex(isStrict); +}); + +addWeekParseToken(['dd', 'ddd', 'dddd'], function (input, week, config, token) { + var weekday = config._locale.weekdaysParse(input, token, config._strict); + // if we didn't get a weekday name, mark the date as invalid + if (weekday != null) { + week.d = weekday; + } else { + getParsingFlags(config).invalidWeekday = input; + } +}); + +addWeekParseToken(['d', 'e', 'E'], function (input, week, config, token) { + week[token] = toInt(input); +}); + +// HELPERS + +function parseWeekday(input, locale) { + if (typeof input !== 'string') { + return input; + } + + if (!isNaN(input)) { + return parseInt(input, 10); + } + + input = locale.weekdaysParse(input); + if (typeof input === 'number') { + return input; + } + + return null; +} + +function parseIsoWeekday(input, locale) { + if (typeof input === 'string') { + return locale.weekdaysParse(input) % 7 || 7; + } + return isNaN(input) ? null : input; +} + +// LOCALES + +var defaultLocaleWeekdays = 'Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday'.split('_'); +function localeWeekdays (m, format) { + if (!m) { + return isArray(this._weekdays) ? this._weekdays : + this._weekdays['standalone']; + } + return isArray(this._weekdays) ? this._weekdays[m.day()] : + this._weekdays[this._weekdays.isFormat.test(format) ? 'format' : 'standalone'][m.day()]; +} + +var defaultLocaleWeekdaysShort = 'Sun_Mon_Tue_Wed_Thu_Fri_Sat'.split('_'); +function localeWeekdaysShort (m) { + return (m) ? this._weekdaysShort[m.day()] : this._weekdaysShort; +} + +var defaultLocaleWeekdaysMin = 'Su_Mo_Tu_We_Th_Fr_Sa'.split('_'); +function localeWeekdaysMin (m) { + return (m) ? this._weekdaysMin[m.day()] : this._weekdaysMin; +} + +function handleStrictParse$1(weekdayName, format, strict) { + var i, ii, mom, llc = weekdayName.toLocaleLowerCase(); + if (!this._weekdaysParse) { + this._weekdaysParse = []; + this._shortWeekdaysParse = []; + this._minWeekdaysParse = []; + + for (i = 0; i < 7; ++i) { + mom = createUTC([2000, 1]).day(i); + this._minWeekdaysParse[i] = this.weekdaysMin(mom, '').toLocaleLowerCase(); + this._shortWeekdaysParse[i] = this.weekdaysShort(mom, '').toLocaleLowerCase(); + this._weekdaysParse[i] = this.weekdays(mom, '').toLocaleLowerCase(); + } + } + + if (strict) { + if (format === 'dddd') { + ii = indexOf.call(this._weekdaysParse, llc); + return ii !== -1 ? ii : null; + } else if (format === 'ddd') { + ii = indexOf.call(this._shortWeekdaysParse, llc); + return ii !== -1 ? ii : null; + } else { + ii = indexOf.call(this._minWeekdaysParse, llc); + return ii !== -1 ? ii : null; + } + } else { + if (format === 'dddd') { + ii = indexOf.call(this._weekdaysParse, llc); + if (ii !== -1) { + return ii; + } + ii = indexOf.call(this._shortWeekdaysParse, llc); + if (ii !== -1) { + return ii; + } + ii = indexOf.call(this._minWeekdaysParse, llc); + return ii !== -1 ? ii : null; + } else if (format === 'ddd') { + ii = indexOf.call(this._shortWeekdaysParse, llc); + if (ii !== -1) { + return ii; + } + ii = indexOf.call(this._weekdaysParse, llc); + if (ii !== -1) { + return ii; + } + ii = indexOf.call(this._minWeekdaysParse, llc); + return ii !== -1 ? ii : null; + } else { + ii = indexOf.call(this._minWeekdaysParse, llc); + if (ii !== -1) { + return ii; + } + ii = indexOf.call(this._weekdaysParse, llc); + if (ii !== -1) { + return ii; + } + ii = indexOf.call(this._shortWeekdaysParse, llc); + return ii !== -1 ? ii : null; + } + } +} + +function localeWeekdaysParse (weekdayName, format, strict) { + var i, mom, regex; + + if (this._weekdaysParseExact) { + return handleStrictParse$1.call(this, weekdayName, format, strict); + } + + if (!this._weekdaysParse) { + this._weekdaysParse = []; + this._minWeekdaysParse = []; + this._shortWeekdaysParse = []; + this._fullWeekdaysParse = []; + } + + for (i = 0; i < 7; i++) { + // make the regex if we don't have it already + + mom = createUTC([2000, 1]).day(i); + if (strict && !this._fullWeekdaysParse[i]) { + this._fullWeekdaysParse[i] = new RegExp('^' + this.weekdays(mom, '').replace('.', '\.?') + '$', 'i'); + this._shortWeekdaysParse[i] = new RegExp('^' + this.weekdaysShort(mom, '').replace('.', '\.?') + '$', 'i'); + this._minWeekdaysParse[i] = new RegExp('^' + this.weekdaysMin(mom, '').replace('.', '\.?') + '$', 'i'); + } + if (!this._weekdaysParse[i]) { + regex = '^' + this.weekdays(mom, '') + '|^' + this.weekdaysShort(mom, '') + '|^' + this.weekdaysMin(mom, ''); + this._weekdaysParse[i] = new RegExp(regex.replace('.', ''), 'i'); + } + // test the regex + if (strict && format === 'dddd' && this._fullWeekdaysParse[i].test(weekdayName)) { + return i; + } else if (strict && format === 'ddd' && this._shortWeekdaysParse[i].test(weekdayName)) { + return i; + } else if (strict && format === 'dd' && this._minWeekdaysParse[i].test(weekdayName)) { + return i; + } else if (!strict && this._weekdaysParse[i].test(weekdayName)) { + return i; + } + } +} + +// MOMENTS + +function getSetDayOfWeek (input) { + if (!this.isValid()) { + return input != null ? this : NaN; + } + var day = this._isUTC ? this._d.getUTCDay() : this._d.getDay(); + if (input != null) { + input = parseWeekday(input, this.localeData()); + return this.add(input - day, 'd'); + } else { + return day; + } +} + +function getSetLocaleDayOfWeek (input) { + if (!this.isValid()) { + return input != null ? this : NaN; + } + var weekday = (this.day() + 7 - this.localeData()._week.dow) % 7; + return input == null ? weekday : this.add(input - weekday, 'd'); +} + +function getSetISODayOfWeek (input) { + if (!this.isValid()) { + return input != null ? this : NaN; + } + + // behaves the same as moment#day except + // as a getter, returns 7 instead of 0 (1-7 range instead of 0-6) + // as a setter, sunday should belong to the previous week. + + if (input != null) { + var weekday = parseIsoWeekday(input, this.localeData()); + return this.day(this.day() % 7 ? weekday : weekday - 7); + } else { + return this.day() || 7; + } +} + +var defaultWeekdaysRegex = matchWord; +function weekdaysRegex (isStrict) { + if (this._weekdaysParseExact) { + if (!hasOwnProp(this, '_weekdaysRegex')) { + computeWeekdaysParse.call(this); + } + if (isStrict) { + return this._weekdaysStrictRegex; + } else { + return this._weekdaysRegex; + } + } else { + if (!hasOwnProp(this, '_weekdaysRegex')) { + this._weekdaysRegex = defaultWeekdaysRegex; + } + return this._weekdaysStrictRegex && isStrict ? + this._weekdaysStrictRegex : this._weekdaysRegex; + } +} + +var defaultWeekdaysShortRegex = matchWord; +function weekdaysShortRegex (isStrict) { + if (this._weekdaysParseExact) { + if (!hasOwnProp(this, '_weekdaysRegex')) { + computeWeekdaysParse.call(this); + } + if (isStrict) { + return this._weekdaysShortStrictRegex; + } else { + return this._weekdaysShortRegex; + } + } else { + if (!hasOwnProp(this, '_weekdaysShortRegex')) { + this._weekdaysShortRegex = defaultWeekdaysShortRegex; + } + return this._weekdaysShortStrictRegex && isStrict ? + this._weekdaysShortStrictRegex : this._weekdaysShortRegex; + } +} + +var defaultWeekdaysMinRegex = matchWord; +function weekdaysMinRegex (isStrict) { + if (this._weekdaysParseExact) { + if (!hasOwnProp(this, '_weekdaysRegex')) { + computeWeekdaysParse.call(this); + } + if (isStrict) { + return this._weekdaysMinStrictRegex; + } else { + return this._weekdaysMinRegex; + } + } else { + if (!hasOwnProp(this, '_weekdaysMinRegex')) { + this._weekdaysMinRegex = defaultWeekdaysMinRegex; + } + return this._weekdaysMinStrictRegex && isStrict ? + this._weekdaysMinStrictRegex : this._weekdaysMinRegex; + } +} + + +function computeWeekdaysParse () { + function cmpLenRev(a, b) { + return b.length - a.length; + } + + var minPieces = [], shortPieces = [], longPieces = [], mixedPieces = [], + i, mom, minp, shortp, longp; + for (i = 0; i < 7; i++) { + // make the regex if we don't have it already + mom = createUTC([2000, 1]).day(i); + minp = this.weekdaysMin(mom, ''); + shortp = this.weekdaysShort(mom, ''); + longp = this.weekdays(mom, ''); + minPieces.push(minp); + shortPieces.push(shortp); + longPieces.push(longp); + mixedPieces.push(minp); + mixedPieces.push(shortp); + mixedPieces.push(longp); + } + // Sorting makes sure if one weekday (or abbr) is a prefix of another it + // will match the longer piece. + minPieces.sort(cmpLenRev); + shortPieces.sort(cmpLenRev); + longPieces.sort(cmpLenRev); + mixedPieces.sort(cmpLenRev); + for (i = 0; i < 7; i++) { + shortPieces[i] = regexEscape(shortPieces[i]); + longPieces[i] = regexEscape(longPieces[i]); + mixedPieces[i] = regexEscape(mixedPieces[i]); + } + + this._weekdaysRegex = new RegExp('^(' + mixedPieces.join('|') + ')', 'i'); + this._weekdaysShortRegex = this._weekdaysRegex; + this._weekdaysMinRegex = this._weekdaysRegex; + + this._weekdaysStrictRegex = new RegExp('^(' + longPieces.join('|') + ')', 'i'); + this._weekdaysShortStrictRegex = new RegExp('^(' + shortPieces.join('|') + ')', 'i'); + this._weekdaysMinStrictRegex = new RegExp('^(' + minPieces.join('|') + ')', 'i'); +} + +// FORMATTING + +function hFormat() { + return this.hours() % 12 || 12; +} + +function kFormat() { + return this.hours() || 24; +} + +addFormatToken('H', ['HH', 2], 0, 'hour'); +addFormatToken('h', ['hh', 2], 0, hFormat); +addFormatToken('k', ['kk', 2], 0, kFormat); + +addFormatToken('hmm', 0, 0, function () { + return '' + hFormat.apply(this) + zeroFill(this.minutes(), 2); +}); + +addFormatToken('hmmss', 0, 0, function () { + return '' + hFormat.apply(this) + zeroFill(this.minutes(), 2) + + zeroFill(this.seconds(), 2); +}); + +addFormatToken('Hmm', 0, 0, function () { + return '' + this.hours() + zeroFill(this.minutes(), 2); +}); + +addFormatToken('Hmmss', 0, 0, function () { + return '' + this.hours() + zeroFill(this.minutes(), 2) + + zeroFill(this.seconds(), 2); +}); + +function meridiem (token, lowercase) { + addFormatToken(token, 0, 0, function () { + return this.localeData().meridiem(this.hours(), this.minutes(), lowercase); + }); +} + +meridiem('a', true); +meridiem('A', false); + +// ALIASES + +addUnitAlias('hour', 'h'); + +// PRIORITY +addUnitPriority('hour', 13); + +// PARSING + +function matchMeridiem (isStrict, locale) { + return locale._meridiemParse; +} + +addRegexToken('a', matchMeridiem); +addRegexToken('A', matchMeridiem); +addRegexToken('H', match1to2); +addRegexToken('h', match1to2); +addRegexToken('k', match1to2); +addRegexToken('HH', match1to2, match2); +addRegexToken('hh', match1to2, match2); +addRegexToken('kk', match1to2, match2); + +addRegexToken('hmm', match3to4); +addRegexToken('hmmss', match5to6); +addRegexToken('Hmm', match3to4); +addRegexToken('Hmmss', match5to6); + +addParseToken(['H', 'HH'], HOUR); +addParseToken(['k', 'kk'], function (input, array, config) { + var kInput = toInt(input); + array[HOUR] = kInput === 24 ? 0 : kInput; +}); +addParseToken(['a', 'A'], function (input, array, config) { + config._isPm = config._locale.isPM(input); + config._meridiem = input; +}); +addParseToken(['h', 'hh'], function (input, array, config) { + array[HOUR] = toInt(input); + getParsingFlags(config).bigHour = true; +}); +addParseToken('hmm', function (input, array, config) { + var pos = input.length - 2; + array[HOUR] = toInt(input.substr(0, pos)); + array[MINUTE] = toInt(input.substr(pos)); + getParsingFlags(config).bigHour = true; +}); +addParseToken('hmmss', function (input, array, config) { + var pos1 = input.length - 4; + var pos2 = input.length - 2; + array[HOUR] = toInt(input.substr(0, pos1)); + array[MINUTE] = toInt(input.substr(pos1, 2)); + array[SECOND] = toInt(input.substr(pos2)); + getParsingFlags(config).bigHour = true; +}); +addParseToken('Hmm', function (input, array, config) { + var pos = input.length - 2; + array[HOUR] = toInt(input.substr(0, pos)); + array[MINUTE] = toInt(input.substr(pos)); +}); +addParseToken('Hmmss', function (input, array, config) { + var pos1 = input.length - 4; + var pos2 = input.length - 2; + array[HOUR] = toInt(input.substr(0, pos1)); + array[MINUTE] = toInt(input.substr(pos1, 2)); + array[SECOND] = toInt(input.substr(pos2)); +}); + +// LOCALES + +function localeIsPM (input) { + // IE8 Quirks Mode & IE7 Standards Mode do not allow accessing strings like arrays + // Using charAt should be more compatible. + return ((input + '').toLowerCase().charAt(0) === 'p'); +} + +var defaultLocaleMeridiemParse = /[ap]\.?m?\.?/i; +function localeMeridiem (hours, minutes, isLower) { + if (hours > 11) { + return isLower ? 'pm' : 'PM'; + } else { + return isLower ? 'am' : 'AM'; + } +} + + +// MOMENTS + +// Setting the hour should keep the time, because the user explicitly +// specified which hour he wants. So trying to maintain the same hour (in +// a new timezone) makes sense. Adding/subtracting hours does not follow +// this rule. +var getSetHour = makeGetSet('Hours', true); + +// months +// week +// weekdays +// meridiem +var baseConfig = { + calendar: defaultCalendar, + longDateFormat: defaultLongDateFormat, + invalidDate: defaultInvalidDate, + ordinal: defaultOrdinal, + dayOfMonthOrdinalParse: defaultDayOfMonthOrdinalParse, + relativeTime: defaultRelativeTime, + + months: defaultLocaleMonths, + monthsShort: defaultLocaleMonthsShort, + + week: defaultLocaleWeek, + + weekdays: defaultLocaleWeekdays, + weekdaysMin: defaultLocaleWeekdaysMin, + weekdaysShort: defaultLocaleWeekdaysShort, + + meridiemParse: defaultLocaleMeridiemParse +}; + +// internal storage for locale config files +var locales = {}; +var localeFamilies = {}; +var globalLocale; + +function normalizeLocale(key) { + return key ? key.toLowerCase().replace('_', '-') : key; +} + +// pick the locale from the array +// try ['en-au', 'en-gb'] as 'en-au', 'en-gb', 'en', as in move through the list trying each +// substring from most specific to least, but move to the next array item if it's a more specific variant than the current root +function chooseLocale(names) { + var i = 0, j, next, locale, split; + + while (i < names.length) { + split = normalizeLocale(names[i]).split('-'); + j = split.length; + next = normalizeLocale(names[i + 1]); + next = next ? next.split('-') : null; + while (j > 0) { + locale = loadLocale(split.slice(0, j).join('-')); + if (locale) { + return locale; + } + if (next && next.length >= j && compareArrays(split, next, true) >= j - 1) { + //the next array item is better than a shallower substring of this one + break; + } + j--; + } + i++; + } + return null; +} + +function loadLocale(name) { + var oldLocale = null; + // TODO: Find a better way to register and load all the locales in Node + if (!locales[name] && (typeof module !== 'undefined') && + module && module.exports) { + try { + oldLocale = globalLocale._abbr; + var aliasedRequire = require; + __webpack_require__(207)("./" + name); + getSetGlobalLocale(oldLocale); + } catch (e) {} + } + return locales[name]; +} + +// This function will load locale and then set the global locale. If +// no arguments are passed in, it will simply return the current global +// locale key. +function getSetGlobalLocale (key, values) { + var data; + if (key) { + if (isUndefined(values)) { + data = getLocale(key); + } + else { + data = defineLocale(key, values); + } + + if (data) { + // moment.duration._locale = moment._locale = data; + globalLocale = data; + } + } + + return globalLocale._abbr; +} + +function defineLocale (name, config) { + if (config !== null) { + var parentConfig = baseConfig; + config.abbr = name; + if (locales[name] != null) { + deprecateSimple('defineLocaleOverride', + 'use moment.updateLocale(localeName, config) to change ' + + 'an existing locale. moment.defineLocale(localeName, ' + + 'config) should only be used for creating a new locale ' + + 'See http://momentjs.com/guides/#/warnings/define-locale/ for more info.'); + parentConfig = locales[name]._config; + } else if (config.parentLocale != null) { + if (locales[config.parentLocale] != null) { + parentConfig = locales[config.parentLocale]._config; + } else { + if (!localeFamilies[config.parentLocale]) { + localeFamilies[config.parentLocale] = []; + } + localeFamilies[config.parentLocale].push({ + name: name, + config: config + }); + return null; + } + } + locales[name] = new Locale(mergeConfigs(parentConfig, config)); + + if (localeFamilies[name]) { + localeFamilies[name].forEach(function (x) { + defineLocale(x.name, x.config); + }); + } + + // backwards compat for now: also set the locale + // make sure we set the locale AFTER all child locales have been + // created, so we won't end up with the child locale set. + getSetGlobalLocale(name); + + + return locales[name]; + } else { + // useful for testing + delete locales[name]; + return null; + } +} + +function updateLocale(name, config) { + if (config != null) { + var locale, tmpLocale, parentConfig = baseConfig; + // MERGE + tmpLocale = loadLocale(name); + if (tmpLocale != null) { + parentConfig = tmpLocale._config; + } + config = mergeConfigs(parentConfig, config); + locale = new Locale(config); + locale.parentLocale = locales[name]; + locales[name] = locale; + + // backwards compat for now: also set the locale + getSetGlobalLocale(name); + } else { + // pass null for config to unupdate, useful for tests + if (locales[name] != null) { + if (locales[name].parentLocale != null) { + locales[name] = locales[name].parentLocale; + } else if (locales[name] != null) { + delete locales[name]; + } + } + } + return locales[name]; +} + +// returns locale data +function getLocale (key) { + var locale; + + if (key && key._locale && key._locale._abbr) { + key = key._locale._abbr; + } + + if (!key) { + return globalLocale; + } + + if (!isArray(key)) { + //short-circuit everything else + locale = loadLocale(key); + if (locale) { + return locale; + } + key = [key]; + } + + return chooseLocale(key); +} + +function listLocales() { + return keys(locales); +} + +function checkOverflow (m) { + var overflow; + var a = m._a; + + if (a && getParsingFlags(m).overflow === -2) { + overflow = + a[MONTH] < 0 || a[MONTH] > 11 ? MONTH : + a[DATE] < 1 || a[DATE] > daysInMonth(a[YEAR], a[MONTH]) ? DATE : + a[HOUR] < 0 || a[HOUR] > 24 || (a[HOUR] === 24 && (a[MINUTE] !== 0 || a[SECOND] !== 0 || a[MILLISECOND] !== 0)) ? HOUR : + a[MINUTE] < 0 || a[MINUTE] > 59 ? MINUTE : + a[SECOND] < 0 || a[SECOND] > 59 ? SECOND : + a[MILLISECOND] < 0 || a[MILLISECOND] > 999 ? MILLISECOND : + -1; + + if (getParsingFlags(m)._overflowDayOfYear && (overflow < YEAR || overflow > DATE)) { + overflow = DATE; + } + if (getParsingFlags(m)._overflowWeeks && overflow === -1) { + overflow = WEEK; + } + if (getParsingFlags(m)._overflowWeekday && overflow === -1) { + overflow = WEEKDAY; + } + + getParsingFlags(m).overflow = overflow; + } + + return m; +} + +// Pick the first defined of two or three arguments. +function defaults(a, b, c) { + if (a != null) { + return a; + } + if (b != null) { + return b; + } + return c; +} + +function currentDateArray(config) { + // hooks is actually the exported moment object + var nowValue = new Date(hooks.now()); + if (config._useUTC) { + return [nowValue.getUTCFullYear(), nowValue.getUTCMonth(), nowValue.getUTCDate()]; + } + return [nowValue.getFullYear(), nowValue.getMonth(), nowValue.getDate()]; +} + +// convert an array to a date. +// the array should mirror the parameters below +// note: all values past the year are optional and will default to the lowest possible value. +// [year, month, day , hour, minute, second, millisecond] +function configFromArray (config) { + var i, date, input = [], currentDate, expectedWeekday, yearToUse; + + if (config._d) { + return; + } + + currentDate = currentDateArray(config); + + //compute day of the year from weeks and weekdays + if (config._w && config._a[DATE] == null && config._a[MONTH] == null) { + dayOfYearFromWeekInfo(config); + } + + //if the day of the year is set, figure out what it is + if (config._dayOfYear != null) { + yearToUse = defaults(config._a[YEAR], currentDate[YEAR]); + + if (config._dayOfYear > daysInYear(yearToUse) || config._dayOfYear === 0) { + getParsingFlags(config)._overflowDayOfYear = true; + } + + date = createUTCDate(yearToUse, 0, config._dayOfYear); + config._a[MONTH] = date.getUTCMonth(); + config._a[DATE] = date.getUTCDate(); + } + + // Default to current date. + // * if no year, month, day of month are given, default to today + // * if day of month is given, default month and year + // * if month is given, default only year + // * if year is given, don't default anything + for (i = 0; i < 3 && config._a[i] == null; ++i) { + config._a[i] = input[i] = currentDate[i]; + } + + // Zero out whatever was not defaulted, including time + for (; i < 7; i++) { + config._a[i] = input[i] = (config._a[i] == null) ? (i === 2 ? 1 : 0) : config._a[i]; + } + + // Check for 24:00:00.000 + if (config._a[HOUR] === 24 && + config._a[MINUTE] === 0 && + config._a[SECOND] === 0 && + config._a[MILLISECOND] === 0) { + config._nextDay = true; + config._a[HOUR] = 0; + } + + config._d = (config._useUTC ? createUTCDate : createDate).apply(null, input); + expectedWeekday = config._useUTC ? config._d.getUTCDay() : config._d.getDay(); + + // Apply timezone offset from input. The actual utcOffset can be changed + // with parseZone. + if (config._tzm != null) { + config._d.setUTCMinutes(config._d.getUTCMinutes() - config._tzm); + } + + if (config._nextDay) { + config._a[HOUR] = 24; + } + + // check for mismatching day of week + if (config._w && typeof config._w.d !== 'undefined' && config._w.d !== expectedWeekday) { + getParsingFlags(config).weekdayMismatch = true; + } +} + +function dayOfYearFromWeekInfo(config) { + var w, weekYear, week, weekday, dow, doy, temp, weekdayOverflow; + + w = config._w; + if (w.GG != null || w.W != null || w.E != null) { + dow = 1; + doy = 4; + + // TODO: We need to take the current isoWeekYear, but that depends on + // how we interpret now (local, utc, fixed offset). So create + // a now version of current config (take local/utc/offset flags, and + // create now). + weekYear = defaults(w.GG, config._a[YEAR], weekOfYear(createLocal(), 1, 4).year); + week = defaults(w.W, 1); + weekday = defaults(w.E, 1); + if (weekday < 1 || weekday > 7) { + weekdayOverflow = true; + } + } else { + dow = config._locale._week.dow; + doy = config._locale._week.doy; + + var curWeek = weekOfYear(createLocal(), dow, doy); + + weekYear = defaults(w.gg, config._a[YEAR], curWeek.year); + + // Default to current week. + week = defaults(w.w, curWeek.week); + + if (w.d != null) { + // weekday -- low day numbers are considered next week + weekday = w.d; + if (weekday < 0 || weekday > 6) { + weekdayOverflow = true; + } + } else if (w.e != null) { + // local weekday -- counting starts from begining of week + weekday = w.e + dow; + if (w.e < 0 || w.e > 6) { + weekdayOverflow = true; + } + } else { + // default to begining of week + weekday = dow; + } + } + if (week < 1 || week > weeksInYear(weekYear, dow, doy)) { + getParsingFlags(config)._overflowWeeks = true; + } else if (weekdayOverflow != null) { + getParsingFlags(config)._overflowWeekday = true; + } else { + temp = dayOfYearFromWeeks(weekYear, week, weekday, dow, doy); + config._a[YEAR] = temp.year; + config._dayOfYear = temp.dayOfYear; + } +} + +// iso 8601 regex +// 0000-00-00 0000-W00 or 0000-W00-0 + T + 00 or 00:00 or 00:00:00 or 00:00:00.000 + +00:00 or +0000 or +00) +var extendedIsoRegex = /^\s*((?:[+-]\d{6}|\d{4})-(?:\d\d-\d\d|W\d\d-\d|W\d\d|\d\d\d|\d\d))(?:(T| )(\d\d(?::\d\d(?::\d\d(?:[.,]\d+)?)?)?)([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?$/; +var basicIsoRegex = /^\s*((?:[+-]\d{6}|\d{4})(?:\d\d\d\d|W\d\d\d|W\d\d|\d\d\d|\d\d))(?:(T| )(\d\d(?:\d\d(?:\d\d(?:[.,]\d+)?)?)?)([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?$/; + +var tzRegex = /Z|[+-]\d\d(?::?\d\d)?/; + +var isoDates = [ + ['YYYYYY-MM-DD', /[+-]\d{6}-\d\d-\d\d/], + ['YYYY-MM-DD', /\d{4}-\d\d-\d\d/], + ['GGGG-[W]WW-E', /\d{4}-W\d\d-\d/], + ['GGGG-[W]WW', /\d{4}-W\d\d/, false], + ['YYYY-DDD', /\d{4}-\d{3}/], + ['YYYY-MM', /\d{4}-\d\d/, false], + ['YYYYYYMMDD', /[+-]\d{10}/], + ['YYYYMMDD', /\d{8}/], + // YYYYMM is NOT allowed by the standard + ['GGGG[W]WWE', /\d{4}W\d{3}/], + ['GGGG[W]WW', /\d{4}W\d{2}/, false], + ['YYYYDDD', /\d{7}/] +]; + +// iso time formats and regexes +var isoTimes = [ + ['HH:mm:ss.SSSS', /\d\d:\d\d:\d\d\.\d+/], + ['HH:mm:ss,SSSS', /\d\d:\d\d:\d\d,\d+/], + ['HH:mm:ss', /\d\d:\d\d:\d\d/], + ['HH:mm', /\d\d:\d\d/], + ['HHmmss.SSSS', /\d\d\d\d\d\d\.\d+/], + ['HHmmss,SSSS', /\d\d\d\d\d\d,\d+/], + ['HHmmss', /\d\d\d\d\d\d/], + ['HHmm', /\d\d\d\d/], + ['HH', /\d\d/] +]; + +var aspNetJsonRegex = /^\/?Date\((\-?\d+)/i; + +// date from iso format +function configFromISO(config) { + var i, l, + string = config._i, + match = extendedIsoRegex.exec(string) || basicIsoRegex.exec(string), + allowTime, dateFormat, timeFormat, tzFormat; + + if (match) { + getParsingFlags(config).iso = true; + + for (i = 0, l = isoDates.length; i < l; i++) { + if (isoDates[i][1].exec(match[1])) { + dateFormat = isoDates[i][0]; + allowTime = isoDates[i][2] !== false; + break; + } + } + if (dateFormat == null) { + config._isValid = false; + return; + } + if (match[3]) { + for (i = 0, l = isoTimes.length; i < l; i++) { + if (isoTimes[i][1].exec(match[3])) { + // match[2] should be 'T' or space + timeFormat = (match[2] || ' ') + isoTimes[i][0]; + break; + } + } + if (timeFormat == null) { + config._isValid = false; + return; + } + } + if (!allowTime && timeFormat != null) { + config._isValid = false; + return; + } + if (match[4]) { + if (tzRegex.exec(match[4])) { + tzFormat = 'Z'; + } else { + config._isValid = false; + return; + } + } + config._f = dateFormat + (timeFormat || '') + (tzFormat || ''); + configFromStringAndFormat(config); + } else { + config._isValid = false; + } +} + +// RFC 2822 regex: For details see https://tools.ietf.org/html/rfc2822#section-3.3 +var rfc2822 = /^(?:(Mon|Tue|Wed|Thu|Fri|Sat|Sun),?\s)?(\d{1,2})\s(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\s(\d{2,4})\s(\d\d):(\d\d)(?::(\d\d))?\s(?:(UT|GMT|[ECMP][SD]T)|([Zz])|([+-]\d{4}))$/; + +function extractFromRFC2822Strings(yearStr, monthStr, dayStr, hourStr, minuteStr, secondStr) { + var result = [ + untruncateYear(yearStr), + defaultLocaleMonthsShort.indexOf(monthStr), + parseInt(dayStr, 10), + parseInt(hourStr, 10), + parseInt(minuteStr, 10) + ]; + + if (secondStr) { + result.push(parseInt(secondStr, 10)); + } + + return result; +} + +function untruncateYear(yearStr) { + var year = parseInt(yearStr, 10); + if (year <= 49) { + return 2000 + year; + } else if (year <= 999) { + return 1900 + year; + } + return year; +} + +function preprocessRFC2822(s) { + // Remove comments and folding whitespace and replace multiple-spaces with a single space + return s.replace(/\([^)]*\)|[\n\t]/g, ' ').replace(/(\s\s+)/g, ' ').trim(); +} + +function checkWeekday(weekdayStr, parsedInput, config) { + if (weekdayStr) { + // TODO: Replace the vanilla JS Date object with an indepentent day-of-week check. + var weekdayProvided = defaultLocaleWeekdaysShort.indexOf(weekdayStr), + weekdayActual = new Date(parsedInput[0], parsedInput[1], parsedInput[2]).getDay(); + if (weekdayProvided !== weekdayActual) { + getParsingFlags(config).weekdayMismatch = true; + config._isValid = false; + return false; + } + } + return true; +} + +var obsOffsets = { + UT: 0, + GMT: 0, + EDT: -4 * 60, + EST: -5 * 60, + CDT: -5 * 60, + CST: -6 * 60, + MDT: -6 * 60, + MST: -7 * 60, + PDT: -7 * 60, + PST: -8 * 60 +}; + +function calculateOffset(obsOffset, militaryOffset, numOffset) { + if (obsOffset) { + return obsOffsets[obsOffset]; + } else if (militaryOffset) { + // the only allowed military tz is Z + return 0; + } else { + var hm = parseInt(numOffset, 10); + var m = hm % 100, h = (hm - m) / 100; + return h * 60 + m; + } +} + +// date and time from ref 2822 format +function configFromRFC2822(config) { + var match = rfc2822.exec(preprocessRFC2822(config._i)); + if (match) { + var parsedArray = extractFromRFC2822Strings(match[4], match[3], match[2], match[5], match[6], match[7]); + if (!checkWeekday(match[1], parsedArray, config)) { + return; + } + + config._a = parsedArray; + config._tzm = calculateOffset(match[8], match[9], match[10]); + + config._d = createUTCDate.apply(null, config._a); + config._d.setUTCMinutes(config._d.getUTCMinutes() - config._tzm); + + getParsingFlags(config).rfc2822 = true; + } else { + config._isValid = false; + } +} + +// date from iso format or fallback +function configFromString(config) { + var matched = aspNetJsonRegex.exec(config._i); + + if (matched !== null) { + config._d = new Date(+matched[1]); + return; + } + + configFromISO(config); + if (config._isValid === false) { + delete config._isValid; + } else { + return; + } + + configFromRFC2822(config); + if (config._isValid === false) { + delete config._isValid; + } else { + return; + } + + // Final attempt, use Input Fallback + hooks.createFromInputFallback(config); +} + +hooks.createFromInputFallback = deprecate( + 'value provided is not in a recognized RFC2822 or ISO format. moment construction falls back to js Date(), ' + + 'which is not reliable across all browsers and versions. Non RFC2822/ISO date formats are ' + + 'discouraged and will be removed in an upcoming major release. Please refer to ' + + 'http://momentjs.com/guides/#/warnings/js-date/ for more info.', + function (config) { + config._d = new Date(config._i + (config._useUTC ? ' UTC' : '')); + } +); + +// constant that refers to the ISO standard +hooks.ISO_8601 = function () {}; + +// constant that refers to the RFC 2822 form +hooks.RFC_2822 = function () {}; + +// date from string and format string +function configFromStringAndFormat(config) { + // TODO: Move this to another part of the creation flow to prevent circular deps + if (config._f === hooks.ISO_8601) { + configFromISO(config); + return; + } + if (config._f === hooks.RFC_2822) { + configFromRFC2822(config); + return; + } + config._a = []; + getParsingFlags(config).empty = true; + + // This array is used to make a Date, either with `new Date` or `Date.UTC` + var string = '' + config._i, + i, parsedInput, tokens, token, skipped, + stringLength = string.length, + totalParsedInputLength = 0; + + tokens = expandFormat(config._f, config._locale).match(formattingTokens) || []; + + for (i = 0; i < tokens.length; i++) { + token = tokens[i]; + parsedInput = (string.match(getParseRegexForToken(token, config)) || [])[0]; + // console.log('token', token, 'parsedInput', parsedInput, + // 'regex', getParseRegexForToken(token, config)); + if (parsedInput) { + skipped = string.substr(0, string.indexOf(parsedInput)); + if (skipped.length > 0) { + getParsingFlags(config).unusedInput.push(skipped); + } + string = string.slice(string.indexOf(parsedInput) + parsedInput.length); + totalParsedInputLength += parsedInput.length; + } + // don't parse if it's not a known token + if (formatTokenFunctions[token]) { + if (parsedInput) { + getParsingFlags(config).empty = false; + } + else { + getParsingFlags(config).unusedTokens.push(token); + } + addTimeToArrayFromToken(token, parsedInput, config); + } + else if (config._strict && !parsedInput) { + getParsingFlags(config).unusedTokens.push(token); + } + } + + // add remaining unparsed input length to the string + getParsingFlags(config).charsLeftOver = stringLength - totalParsedInputLength; + if (string.length > 0) { + getParsingFlags(config).unusedInput.push(string); + } + + // clear _12h flag if hour is <= 12 + if (config._a[HOUR] <= 12 && + getParsingFlags(config).bigHour === true && + config._a[HOUR] > 0) { + getParsingFlags(config).bigHour = undefined; + } + + getParsingFlags(config).parsedDateParts = config._a.slice(0); + getParsingFlags(config).meridiem = config._meridiem; + // handle meridiem + config._a[HOUR] = meridiemFixWrap(config._locale, config._a[HOUR], config._meridiem); + + configFromArray(config); + checkOverflow(config); +} + + +function meridiemFixWrap (locale, hour, meridiem) { + var isPm; + + if (meridiem == null) { + // nothing to do + return hour; + } + if (locale.meridiemHour != null) { + return locale.meridiemHour(hour, meridiem); + } else if (locale.isPM != null) { + // Fallback + isPm = locale.isPM(meridiem); + if (isPm && hour < 12) { + hour += 12; + } + if (!isPm && hour === 12) { + hour = 0; + } + return hour; + } else { + // this is not supposed to happen + return hour; + } +} + +// date from string and array of format strings +function configFromStringAndArray(config) { + var tempConfig, + bestMoment, + + scoreToBeat, + i, + currentScore; + + if (config._f.length === 0) { + getParsingFlags(config).invalidFormat = true; + config._d = new Date(NaN); + return; + } + + for (i = 0; i < config._f.length; i++) { + currentScore = 0; + tempConfig = copyConfig({}, config); + if (config._useUTC != null) { + tempConfig._useUTC = config._useUTC; + } + tempConfig._f = config._f[i]; + configFromStringAndFormat(tempConfig); + + if (!isValid(tempConfig)) { + continue; + } + + // if there is any input that was not parsed add a penalty for that format + currentScore += getParsingFlags(tempConfig).charsLeftOver; + + //or tokens + currentScore += getParsingFlags(tempConfig).unusedTokens.length * 10; + + getParsingFlags(tempConfig).score = currentScore; + + if (scoreToBeat == null || currentScore < scoreToBeat) { + scoreToBeat = currentScore; + bestMoment = tempConfig; + } + } + + extend(config, bestMoment || tempConfig); +} + +function configFromObject(config) { + if (config._d) { + return; + } + + var i = normalizeObjectUnits(config._i); + config._a = map([i.year, i.month, i.day || i.date, i.hour, i.minute, i.second, i.millisecond], function (obj) { + return obj && parseInt(obj, 10); + }); + + configFromArray(config); +} + +function createFromConfig (config) { + var res = new Moment(checkOverflow(prepareConfig(config))); + if (res._nextDay) { + // Adding is smart enough around DST + res.add(1, 'd'); + res._nextDay = undefined; + } + + return res; +} + +function prepareConfig (config) { + var input = config._i, + format = config._f; + + config._locale = config._locale || getLocale(config._l); + + if (input === null || (format === undefined && input === '')) { + return createInvalid({nullInput: true}); + } + + if (typeof input === 'string') { + config._i = input = config._locale.preparse(input); + } + + if (isMoment(input)) { + return new Moment(checkOverflow(input)); + } else if (isDate(input)) { + config._d = input; + } else if (isArray(format)) { + configFromStringAndArray(config); + } else if (format) { + configFromStringAndFormat(config); + } else { + configFromInput(config); + } + + if (!isValid(config)) { + config._d = null; + } + + return config; +} + +function configFromInput(config) { + var input = config._i; + if (isUndefined(input)) { + config._d = new Date(hooks.now()); + } else if (isDate(input)) { + config._d = new Date(input.valueOf()); + } else if (typeof input === 'string') { + configFromString(config); + } else if (isArray(input)) { + config._a = map(input.slice(0), function (obj) { + return parseInt(obj, 10); + }); + configFromArray(config); + } else if (isObject(input)) { + configFromObject(config); + } else if (isNumber(input)) { + // from milliseconds + config._d = new Date(input); + } else { + hooks.createFromInputFallback(config); + } +} + +function createLocalOrUTC (input, format, locale, strict, isUTC) { + var c = {}; + + if (locale === true || locale === false) { + strict = locale; + locale = undefined; + } + + if ((isObject(input) && isObjectEmpty(input)) || + (isArray(input) && input.length === 0)) { + input = undefined; + } + // object construction must be done this way. + // https://github.com/moment/moment/issues/1423 + c._isAMomentObject = true; + c._useUTC = c._isUTC = isUTC; + c._l = locale; + c._i = input; + c._f = format; + c._strict = strict; + + return createFromConfig(c); +} + +function createLocal (input, format, locale, strict) { + return createLocalOrUTC(input, format, locale, strict, false); +} + +var prototypeMin = deprecate( + 'moment().min is deprecated, use moment.max instead. http://momentjs.com/guides/#/warnings/min-max/', + function () { + var other = createLocal.apply(null, arguments); + if (this.isValid() && other.isValid()) { + return other < this ? this : other; + } else { + return createInvalid(); + } + } +); + +var prototypeMax = deprecate( + 'moment().max is deprecated, use moment.min instead. http://momentjs.com/guides/#/warnings/min-max/', + function () { + var other = createLocal.apply(null, arguments); + if (this.isValid() && other.isValid()) { + return other > this ? this : other; + } else { + return createInvalid(); + } + } +); + +// Pick a moment m from moments so that m[fn](other) is true for all +// other. This relies on the function fn to be transitive. +// +// moments should either be an array of moment objects or an array, whose +// first element is an array of moment objects. +function pickBy(fn, moments) { + var res, i; + if (moments.length === 1 && isArray(moments[0])) { + moments = moments[0]; + } + if (!moments.length) { + return createLocal(); + } + res = moments[0]; + for (i = 1; i < moments.length; ++i) { + if (!moments[i].isValid() || moments[i][fn](res)) { + res = moments[i]; + } + } + return res; +} + +// TODO: Use [].sort instead? +function min () { + var args = [].slice.call(arguments, 0); + + return pickBy('isBefore', args); +} + +function max () { + var args = [].slice.call(arguments, 0); + + return pickBy('isAfter', args); +} + +var now = function () { + return Date.now ? Date.now() : +(new Date()); +}; + +var ordering = ['year', 'quarter', 'month', 'week', 'day', 'hour', 'minute', 'second', 'millisecond']; + +function isDurationValid(m) { + for (var key in m) { + if (!(indexOf.call(ordering, key) !== -1 && (m[key] == null || !isNaN(m[key])))) { + return false; + } + } + + var unitHasDecimal = false; + for (var i = 0; i < ordering.length; ++i) { + if (m[ordering[i]]) { + if (unitHasDecimal) { + return false; // only allow non-integers for smallest unit + } + if (parseFloat(m[ordering[i]]) !== toInt(m[ordering[i]])) { + unitHasDecimal = true; + } + } + } + + return true; +} + +function isValid$1() { + return this._isValid; +} + +function createInvalid$1() { + return createDuration(NaN); +} + +function Duration (duration) { + var normalizedInput = normalizeObjectUnits(duration), + years = normalizedInput.year || 0, + quarters = normalizedInput.quarter || 0, + months = normalizedInput.month || 0, + weeks = normalizedInput.week || 0, + days = normalizedInput.day || 0, + hours = normalizedInput.hour || 0, + minutes = normalizedInput.minute || 0, + seconds = normalizedInput.second || 0, + milliseconds = normalizedInput.millisecond || 0; + + this._isValid = isDurationValid(normalizedInput); + + // representation for dateAddRemove + this._milliseconds = +milliseconds + + seconds * 1e3 + // 1000 + minutes * 6e4 + // 1000 * 60 + hours * 1000 * 60 * 60; //using 1000 * 60 * 60 instead of 36e5 to avoid floating point rounding errors https://github.com/moment/moment/issues/2978 + // Because of dateAddRemove treats 24 hours as different from a + // day when working around DST, we need to store them separately + this._days = +days + + weeks * 7; + // It is impossible to translate months into days without knowing + // which months you are are talking about, so we have to store + // it separately. + this._months = +months + + quarters * 3 + + years * 12; + + this._data = {}; + + this._locale = getLocale(); + + this._bubble(); +} + +function isDuration (obj) { + return obj instanceof Duration; +} + +function absRound (number) { + if (number < 0) { + return Math.round(-1 * number) * -1; + } else { + return Math.round(number); + } +} + +// FORMATTING + +function offset (token, separator) { + addFormatToken(token, 0, 0, function () { + var offset = this.utcOffset(); + var sign = '+'; + if (offset < 0) { + offset = -offset; + sign = '-'; + } + return sign + zeroFill(~~(offset / 60), 2) + separator + zeroFill(~~(offset) % 60, 2); + }); +} + +offset('Z', ':'); +offset('ZZ', ''); + +// PARSING + +addRegexToken('Z', matchShortOffset); +addRegexToken('ZZ', matchShortOffset); +addParseToken(['Z', 'ZZ'], function (input, array, config) { + config._useUTC = true; + config._tzm = offsetFromString(matchShortOffset, input); +}); + +// HELPERS + +// timezone chunker +// '+10:00' > ['10', '00'] +// '-1530' > ['-15', '30'] +var chunkOffset = /([\+\-]|\d\d)/gi; + +function offsetFromString(matcher, string) { + var matches = (string || '').match(matcher); + + if (matches === null) { + return null; + } + + var chunk = matches[matches.length - 1] || []; + var parts = (chunk + '').match(chunkOffset) || ['-', 0, 0]; + var minutes = +(parts[1] * 60) + toInt(parts[2]); + + return minutes === 0 ? + 0 : + parts[0] === '+' ? minutes : -minutes; +} + +// Return a moment from input, that is local/utc/zone equivalent to model. +function cloneWithOffset(input, model) { + var res, diff; + if (model._isUTC) { + res = model.clone(); + diff = (isMoment(input) || isDate(input) ? input.valueOf() : createLocal(input).valueOf()) - res.valueOf(); + // Use low-level api, because this fn is low-level api. + res._d.setTime(res._d.valueOf() + diff); + hooks.updateOffset(res, false); + return res; + } else { + return createLocal(input).local(); + } +} + +function getDateOffset (m) { + // On Firefox.24 Date#getTimezoneOffset returns a floating point. + // https://github.com/moment/moment/pull/1871 + return -Math.round(m._d.getTimezoneOffset() / 15) * 15; +} + +// HOOKS + +// This function will be called whenever a moment is mutated. +// It is intended to keep the offset in sync with the timezone. +hooks.updateOffset = function () {}; + +// MOMENTS + +// keepLocalTime = true means only change the timezone, without +// affecting the local hour. So 5:31:26 +0300 --[utcOffset(2, true)]--> +// 5:31:26 +0200 It is possible that 5:31:26 doesn't exist with offset +// +0200, so we adjust the time as needed, to be valid. +// +// Keeping the time actually adds/subtracts (one hour) +// from the actual represented time. That is why we call updateOffset +// a second time. In case it wants us to change the offset again +// _changeInProgress == true case, then we have to adjust, because +// there is no such time in the given timezone. +function getSetOffset (input, keepLocalTime, keepMinutes) { + var offset = this._offset || 0, + localAdjust; + if (!this.isValid()) { + return input != null ? this : NaN; + } + if (input != null) { + if (typeof input === 'string') { + input = offsetFromString(matchShortOffset, input); + if (input === null) { + return this; + } + } else if (Math.abs(input) < 16 && !keepMinutes) { + input = input * 60; + } + if (!this._isUTC && keepLocalTime) { + localAdjust = getDateOffset(this); + } + this._offset = input; + this._isUTC = true; + if (localAdjust != null) { + this.add(localAdjust, 'm'); + } + if (offset !== input) { + if (!keepLocalTime || this._changeInProgress) { + addSubtract(this, createDuration(input - offset, 'm'), 1, false); + } else if (!this._changeInProgress) { + this._changeInProgress = true; + hooks.updateOffset(this, true); + this._changeInProgress = null; + } + } + return this; + } else { + return this._isUTC ? offset : getDateOffset(this); + } +} + +function getSetZone (input, keepLocalTime) { + if (input != null) { + if (typeof input !== 'string') { + input = -input; + } + + this.utcOffset(input, keepLocalTime); + + return this; + } else { + return -this.utcOffset(); + } +} + +function setOffsetToUTC (keepLocalTime) { + return this.utcOffset(0, keepLocalTime); +} + +function setOffsetToLocal (keepLocalTime) { + if (this._isUTC) { + this.utcOffset(0, keepLocalTime); + this._isUTC = false; + + if (keepLocalTime) { + this.subtract(getDateOffset(this), 'm'); + } + } + return this; +} + +function setOffsetToParsedOffset () { + if (this._tzm != null) { + this.utcOffset(this._tzm, false, true); + } else if (typeof this._i === 'string') { + var tZone = offsetFromString(matchOffset, this._i); + if (tZone != null) { + this.utcOffset(tZone); + } + else { + this.utcOffset(0, true); + } + } + return this; +} + +function hasAlignedHourOffset (input) { + if (!this.isValid()) { + return false; + } + input = input ? createLocal(input).utcOffset() : 0; + + return (this.utcOffset() - input) % 60 === 0; +} + +function isDaylightSavingTime () { + return ( + this.utcOffset() > this.clone().month(0).utcOffset() || + this.utcOffset() > this.clone().month(5).utcOffset() + ); +} + +function isDaylightSavingTimeShifted () { + if (!isUndefined(this._isDSTShifted)) { + return this._isDSTShifted; + } + + var c = {}; + + copyConfig(c, this); + c = prepareConfig(c); + + if (c._a) { + var other = c._isUTC ? createUTC(c._a) : createLocal(c._a); + this._isDSTShifted = this.isValid() && + compareArrays(c._a, other.toArray()) > 0; + } else { + this._isDSTShifted = false; + } + + return this._isDSTShifted; +} + +function isLocal () { + return this.isValid() ? !this._isUTC : false; +} + +function isUtcOffset () { + return this.isValid() ? this._isUTC : false; +} + +function isUtc () { + return this.isValid() ? this._isUTC && this._offset === 0 : false; +} + +// ASP.NET json date format regex +var aspNetRegex = /^(\-|\+)?(?:(\d*)[. ])?(\d+)\:(\d+)(?:\:(\d+)(\.\d*)?)?$/; + +// from http://docs.closure-library.googlecode.com/git/closure_goog_date_date.js.source.html +// somewhat more in line with 4.4.3.2 2004 spec, but allows decimal anywhere +// and further modified to allow for strings containing both week and day +var isoRegex = /^(-|\+)?P(?:([-+]?[0-9,.]*)Y)?(?:([-+]?[0-9,.]*)M)?(?:([-+]?[0-9,.]*)W)?(?:([-+]?[0-9,.]*)D)?(?:T(?:([-+]?[0-9,.]*)H)?(?:([-+]?[0-9,.]*)M)?(?:([-+]?[0-9,.]*)S)?)?$/; + +function createDuration (input, key) { + var duration = input, + // matching against regexp is expensive, do it on demand + match = null, + sign, + ret, + diffRes; + + if (isDuration(input)) { + duration = { + ms : input._milliseconds, + d : input._days, + M : input._months + }; + } else if (isNumber(input)) { + duration = {}; + if (key) { + duration[key] = input; + } else { + duration.milliseconds = input; + } + } else if (!!(match = aspNetRegex.exec(input))) { + sign = (match[1] === '-') ? -1 : 1; + duration = { + y : 0, + d : toInt(match[DATE]) * sign, + h : toInt(match[HOUR]) * sign, + m : toInt(match[MINUTE]) * sign, + s : toInt(match[SECOND]) * sign, + ms : toInt(absRound(match[MILLISECOND] * 1000)) * sign // the millisecond decimal point is included in the match + }; + } else if (!!(match = isoRegex.exec(input))) { + sign = (match[1] === '-') ? -1 : (match[1] === '+') ? 1 : 1; + duration = { + y : parseIso(match[2], sign), + M : parseIso(match[3], sign), + w : parseIso(match[4], sign), + d : parseIso(match[5], sign), + h : parseIso(match[6], sign), + m : parseIso(match[7], sign), + s : parseIso(match[8], sign) + }; + } else if (duration == null) {// checks for null or undefined + duration = {}; + } else if (typeof duration === 'object' && ('from' in duration || 'to' in duration)) { + diffRes = momentsDifference(createLocal(duration.from), createLocal(duration.to)); + + duration = {}; + duration.ms = diffRes.milliseconds; + duration.M = diffRes.months; + } + + ret = new Duration(duration); + + if (isDuration(input) && hasOwnProp(input, '_locale')) { + ret._locale = input._locale; + } + + return ret; +} + +createDuration.fn = Duration.prototype; +createDuration.invalid = createInvalid$1; + +function parseIso (inp, sign) { + // We'd normally use ~~inp for this, but unfortunately it also + // converts floats to ints. + // inp may be undefined, so careful calling replace on it. + var res = inp && parseFloat(inp.replace(',', '.')); + // apply sign while we're at it + return (isNaN(res) ? 0 : res) * sign; +} + +function positiveMomentsDifference(base, other) { + var res = {milliseconds: 0, months: 0}; + + res.months = other.month() - base.month() + + (other.year() - base.year()) * 12; + if (base.clone().add(res.months, 'M').isAfter(other)) { + --res.months; + } + + res.milliseconds = +other - +(base.clone().add(res.months, 'M')); + + return res; +} + +function momentsDifference(base, other) { + var res; + if (!(base.isValid() && other.isValid())) { + return {milliseconds: 0, months: 0}; + } + + other = cloneWithOffset(other, base); + if (base.isBefore(other)) { + res = positiveMomentsDifference(base, other); + } else { + res = positiveMomentsDifference(other, base); + res.milliseconds = -res.milliseconds; + res.months = -res.months; + } + + return res; +} + +// TODO: remove 'name' arg after deprecation is removed +function createAdder(direction, name) { + return function (val, period) { + var dur, tmp; + //invert the arguments, but complain about it + if (period !== null && !isNaN(+period)) { + deprecateSimple(name, 'moment().' + name + '(period, number) is deprecated. Please use moment().' + name + '(number, period). ' + + 'See http://momentjs.com/guides/#/warnings/add-inverted-param/ for more info.'); + tmp = val; val = period; period = tmp; + } + + val = typeof val === 'string' ? +val : val; + dur = createDuration(val, period); + addSubtract(this, dur, direction); + return this; + }; +} + +function addSubtract (mom, duration, isAdding, updateOffset) { + var milliseconds = duration._milliseconds, + days = absRound(duration._days), + months = absRound(duration._months); + + if (!mom.isValid()) { + // No op + return; + } + + updateOffset = updateOffset == null ? true : updateOffset; + + if (months) { + setMonth(mom, get(mom, 'Month') + months * isAdding); + } + if (days) { + set$1(mom, 'Date', get(mom, 'Date') + days * isAdding); + } + if (milliseconds) { + mom._d.setTime(mom._d.valueOf() + milliseconds * isAdding); + } + if (updateOffset) { + hooks.updateOffset(mom, days || months); + } +} + +var add = createAdder(1, 'add'); +var subtract = createAdder(-1, 'subtract'); + +function getCalendarFormat(myMoment, now) { + var diff = myMoment.diff(now, 'days', true); + return diff < -6 ? 'sameElse' : + diff < -1 ? 'lastWeek' : + diff < 0 ? 'lastDay' : + diff < 1 ? 'sameDay' : + diff < 2 ? 'nextDay' : + diff < 7 ? 'nextWeek' : 'sameElse'; +} + +function calendar$1 (time, formats) { + // We want to compare the start of today, vs this. + // Getting start-of-today depends on whether we're local/utc/offset or not. + var now = time || createLocal(), + sod = cloneWithOffset(now, this).startOf('day'), + format = hooks.calendarFormat(this, sod) || 'sameElse'; + + var output = formats && (isFunction(formats[format]) ? formats[format].call(this, now) : formats[format]); + + return this.format(output || this.localeData().calendar(format, this, createLocal(now))); +} + +function clone () { + return new Moment(this); +} + +function isAfter (input, units) { + var localInput = isMoment(input) ? input : createLocal(input); + if (!(this.isValid() && localInput.isValid())) { + return false; + } + units = normalizeUnits(!isUndefined(units) ? units : 'millisecond'); + if (units === 'millisecond') { + return this.valueOf() > localInput.valueOf(); + } else { + return localInput.valueOf() < this.clone().startOf(units).valueOf(); + } +} + +function isBefore (input, units) { + var localInput = isMoment(input) ? input : createLocal(input); + if (!(this.isValid() && localInput.isValid())) { + return false; + } + units = normalizeUnits(!isUndefined(units) ? units : 'millisecond'); + if (units === 'millisecond') { + return this.valueOf() < localInput.valueOf(); + } else { + return this.clone().endOf(units).valueOf() < localInput.valueOf(); + } +} + +function isBetween (from, to, units, inclusivity) { + inclusivity = inclusivity || '()'; + return (inclusivity[0] === '(' ? this.isAfter(from, units) : !this.isBefore(from, units)) && + (inclusivity[1] === ')' ? this.isBefore(to, units) : !this.isAfter(to, units)); +} + +function isSame (input, units) { + var localInput = isMoment(input) ? input : createLocal(input), + inputMs; + if (!(this.isValid() && localInput.isValid())) { + return false; + } + units = normalizeUnits(units || 'millisecond'); + if (units === 'millisecond') { + return this.valueOf() === localInput.valueOf(); + } else { + inputMs = localInput.valueOf(); + return this.clone().startOf(units).valueOf() <= inputMs && inputMs <= this.clone().endOf(units).valueOf(); + } +} + +function isSameOrAfter (input, units) { + return this.isSame(input, units) || this.isAfter(input,units); +} + +function isSameOrBefore (input, units) { + return this.isSame(input, units) || this.isBefore(input,units); +} + +function diff (input, units, asFloat) { + var that, + zoneDelta, + delta, output; + + if (!this.isValid()) { + return NaN; + } + + that = cloneWithOffset(input, this); + + if (!that.isValid()) { + return NaN; + } + + zoneDelta = (that.utcOffset() - this.utcOffset()) * 6e4; + + units = normalizeUnits(units); + + switch (units) { + case 'year': output = monthDiff(this, that) / 12; break; + case 'month': output = monthDiff(this, that); break; + case 'quarter': output = monthDiff(this, that) / 3; break; + case 'second': output = (this - that) / 1e3; break; // 1000 + case 'minute': output = (this - that) / 6e4; break; // 1000 * 60 + case 'hour': output = (this - that) / 36e5; break; // 1000 * 60 * 60 + case 'day': output = (this - that - zoneDelta) / 864e5; break; // 1000 * 60 * 60 * 24, negate dst + case 'week': output = (this - that - zoneDelta) / 6048e5; break; // 1000 * 60 * 60 * 24 * 7, negate dst + default: output = this - that; + } + + return asFloat ? output : absFloor(output); +} + +function monthDiff (a, b) { + // difference in months + var wholeMonthDiff = ((b.year() - a.year()) * 12) + (b.month() - a.month()), + // b is in (anchor - 1 month, anchor + 1 month) + anchor = a.clone().add(wholeMonthDiff, 'months'), + anchor2, adjust; + + if (b - anchor < 0) { + anchor2 = a.clone().add(wholeMonthDiff - 1, 'months'); + // linear across the month + adjust = (b - anchor) / (anchor - anchor2); + } else { + anchor2 = a.clone().add(wholeMonthDiff + 1, 'months'); + // linear across the month + adjust = (b - anchor) / (anchor2 - anchor); + } + + //check for negative zero, return zero if negative zero + return -(wholeMonthDiff + adjust) || 0; +} + +hooks.defaultFormat = 'YYYY-MM-DDTHH:mm:ssZ'; +hooks.defaultFormatUtc = 'YYYY-MM-DDTHH:mm:ss[Z]'; + +function toString () { + return this.clone().locale('en').format('ddd MMM DD YYYY HH:mm:ss [GMT]ZZ'); +} + +function toISOString(keepOffset) { + if (!this.isValid()) { + return null; + } + var utc = keepOffset !== true; + var m = utc ? this.clone().utc() : this; + if (m.year() < 0 || m.year() > 9999) { + return formatMoment(m, utc ? 'YYYYYY-MM-DD[T]HH:mm:ss.SSS[Z]' : 'YYYYYY-MM-DD[T]HH:mm:ss.SSSZ'); + } + if (isFunction(Date.prototype.toISOString)) { + // native implementation is ~50x faster, use it when we can + if (utc) { + return this.toDate().toISOString(); + } else { + return new Date(this._d.valueOf()).toISOString().replace('Z', formatMoment(m, 'Z')); + } + } + return formatMoment(m, utc ? 'YYYY-MM-DD[T]HH:mm:ss.SSS[Z]' : 'YYYY-MM-DD[T]HH:mm:ss.SSSZ'); +} + +/** + * Return a human readable representation of a moment that can + * also be evaluated to get a new moment which is the same + * + * @link https://nodejs.org/dist/latest/docs/api/util.html#util_custom_inspect_function_on_objects + */ +function inspect () { + if (!this.isValid()) { + return 'moment.invalid(/* ' + this._i + ' */)'; + } + var func = 'moment'; + var zone = ''; + if (!this.isLocal()) { + func = this.utcOffset() === 0 ? 'moment.utc' : 'moment.parseZone'; + zone = 'Z'; + } + var prefix = '[' + func + '("]'; + var year = (0 <= this.year() && this.year() <= 9999) ? 'YYYY' : 'YYYYYY'; + var datetime = '-MM-DD[T]HH:mm:ss.SSS'; + var suffix = zone + '[")]'; + + return this.format(prefix + year + datetime + suffix); +} + +function format (inputString) { + if (!inputString) { + inputString = this.isUtc() ? hooks.defaultFormatUtc : hooks.defaultFormat; + } + var output = formatMoment(this, inputString); + return this.localeData().postformat(output); +} + +function from (time, withoutSuffix) { + if (this.isValid() && + ((isMoment(time) && time.isValid()) || + createLocal(time).isValid())) { + return createDuration({to: this, from: time}).locale(this.locale()).humanize(!withoutSuffix); + } else { + return this.localeData().invalidDate(); + } +} + +function fromNow (withoutSuffix) { + return this.from(createLocal(), withoutSuffix); +} + +function to (time, withoutSuffix) { + if (this.isValid() && + ((isMoment(time) && time.isValid()) || + createLocal(time).isValid())) { + return createDuration({from: this, to: time}).locale(this.locale()).humanize(!withoutSuffix); + } else { + return this.localeData().invalidDate(); + } +} + +function toNow (withoutSuffix) { + return this.to(createLocal(), withoutSuffix); +} + +// If passed a locale key, it will set the locale for this +// instance. Otherwise, it will return the locale configuration +// variables for this instance. +function locale (key) { + var newLocaleData; + + if (key === undefined) { + return this._locale._abbr; + } else { + newLocaleData = getLocale(key); + if (newLocaleData != null) { + this._locale = newLocaleData; + } + return this; + } +} + +var lang = deprecate( + 'moment().lang() is deprecated. Instead, use moment().localeData() to get the language configuration. Use moment().locale() to change languages.', + function (key) { + if (key === undefined) { + return this.localeData(); + } else { + return this.locale(key); + } + } +); + +function localeData () { + return this._locale; +} + +function startOf (units) { + units = normalizeUnits(units); + // the following switch intentionally omits break keywords + // to utilize falling through the cases. + switch (units) { + case 'year': + this.month(0); + /* falls through */ + case 'quarter': + case 'month': + this.date(1); + /* falls through */ + case 'week': + case 'isoWeek': + case 'day': + case 'date': + this.hours(0); + /* falls through */ + case 'hour': + this.minutes(0); + /* falls through */ + case 'minute': + this.seconds(0); + /* falls through */ + case 'second': + this.milliseconds(0); + } + + // weeks are a special case + if (units === 'week') { + this.weekday(0); + } + if (units === 'isoWeek') { + this.isoWeekday(1); + } + + // quarters are also special + if (units === 'quarter') { + this.month(Math.floor(this.month() / 3) * 3); + } + + return this; +} + +function endOf (units) { + units = normalizeUnits(units); + if (units === undefined || units === 'millisecond') { + return this; + } + + // 'date' is an alias for 'day', so it should be considered as such. + if (units === 'date') { + units = 'day'; + } + + return this.startOf(units).add(1, (units === 'isoWeek' ? 'week' : units)).subtract(1, 'ms'); +} + +function valueOf () { + return this._d.valueOf() - ((this._offset || 0) * 60000); +} + +function unix () { + return Math.floor(this.valueOf() / 1000); +} + +function toDate () { + return new Date(this.valueOf()); +} + +function toArray () { + var m = this; + return [m.year(), m.month(), m.date(), m.hour(), m.minute(), m.second(), m.millisecond()]; +} + +function toObject () { + var m = this; + return { + years: m.year(), + months: m.month(), + date: m.date(), + hours: m.hours(), + minutes: m.minutes(), + seconds: m.seconds(), + milliseconds: m.milliseconds() + }; +} + +function toJSON () { + // new Date(NaN).toJSON() === null + return this.isValid() ? this.toISOString() : null; +} + +function isValid$2 () { + return isValid(this); +} + +function parsingFlags () { + return extend({}, getParsingFlags(this)); +} + +function invalidAt () { + return getParsingFlags(this).overflow; +} + +function creationData() { + return { + input: this._i, + format: this._f, + locale: this._locale, + isUTC: this._isUTC, + strict: this._strict + }; +} + +// FORMATTING + +addFormatToken(0, ['gg', 2], 0, function () { + return this.weekYear() % 100; +}); + +addFormatToken(0, ['GG', 2], 0, function () { + return this.isoWeekYear() % 100; +}); + +function addWeekYearFormatToken (token, getter) { + addFormatToken(0, [token, token.length], 0, getter); +} + +addWeekYearFormatToken('gggg', 'weekYear'); +addWeekYearFormatToken('ggggg', 'weekYear'); +addWeekYearFormatToken('GGGG', 'isoWeekYear'); +addWeekYearFormatToken('GGGGG', 'isoWeekYear'); + +// ALIASES + +addUnitAlias('weekYear', 'gg'); +addUnitAlias('isoWeekYear', 'GG'); + +// PRIORITY + +addUnitPriority('weekYear', 1); +addUnitPriority('isoWeekYear', 1); + + +// PARSING + +addRegexToken('G', matchSigned); +addRegexToken('g', matchSigned); +addRegexToken('GG', match1to2, match2); +addRegexToken('gg', match1to2, match2); +addRegexToken('GGGG', match1to4, match4); +addRegexToken('gggg', match1to4, match4); +addRegexToken('GGGGG', match1to6, match6); +addRegexToken('ggggg', match1to6, match6); + +addWeekParseToken(['gggg', 'ggggg', 'GGGG', 'GGGGG'], function (input, week, config, token) { + week[token.substr(0, 2)] = toInt(input); +}); + +addWeekParseToken(['gg', 'GG'], function (input, week, config, token) { + week[token] = hooks.parseTwoDigitYear(input); +}); + +// MOMENTS + +function getSetWeekYear (input) { + return getSetWeekYearHelper.call(this, + input, + this.week(), + this.weekday(), + this.localeData()._week.dow, + this.localeData()._week.doy); +} + +function getSetISOWeekYear (input) { + return getSetWeekYearHelper.call(this, + input, this.isoWeek(), this.isoWeekday(), 1, 4); +} + +function getISOWeeksInYear () { + return weeksInYear(this.year(), 1, 4); +} + +function getWeeksInYear () { + var weekInfo = this.localeData()._week; + return weeksInYear(this.year(), weekInfo.dow, weekInfo.doy); +} + +function getSetWeekYearHelper(input, week, weekday, dow, doy) { + var weeksTarget; + if (input == null) { + return weekOfYear(this, dow, doy).year; + } else { + weeksTarget = weeksInYear(input, dow, doy); + if (week > weeksTarget) { + week = weeksTarget; + } + return setWeekAll.call(this, input, week, weekday, dow, doy); + } +} + +function setWeekAll(weekYear, week, weekday, dow, doy) { + var dayOfYearData = dayOfYearFromWeeks(weekYear, week, weekday, dow, doy), + date = createUTCDate(dayOfYearData.year, 0, dayOfYearData.dayOfYear); + + this.year(date.getUTCFullYear()); + this.month(date.getUTCMonth()); + this.date(date.getUTCDate()); + return this; +} + +// FORMATTING + +addFormatToken('Q', 0, 'Qo', 'quarter'); + +// ALIASES + +addUnitAlias('quarter', 'Q'); + +// PRIORITY + +addUnitPriority('quarter', 7); + +// PARSING + +addRegexToken('Q', match1); +addParseToken('Q', function (input, array) { + array[MONTH] = (toInt(input) - 1) * 3; +}); + +// MOMENTS + +function getSetQuarter (input) { + return input == null ? Math.ceil((this.month() + 1) / 3) : this.month((input - 1) * 3 + this.month() % 3); +} + +// FORMATTING + +addFormatToken('D', ['DD', 2], 'Do', 'date'); + +// ALIASES + +addUnitAlias('date', 'D'); + +// PRIOROITY +addUnitPriority('date', 9); + +// PARSING + +addRegexToken('D', match1to2); +addRegexToken('DD', match1to2, match2); +addRegexToken('Do', function (isStrict, locale) { + // TODO: Remove "ordinalParse" fallback in next major release. + return isStrict ? + (locale._dayOfMonthOrdinalParse || locale._ordinalParse) : + locale._dayOfMonthOrdinalParseLenient; +}); + +addParseToken(['D', 'DD'], DATE); +addParseToken('Do', function (input, array) { + array[DATE] = toInt(input.match(match1to2)[0]); +}); + +// MOMENTS + +var getSetDayOfMonth = makeGetSet('Date', true); + +// FORMATTING + +addFormatToken('DDD', ['DDDD', 3], 'DDDo', 'dayOfYear'); + +// ALIASES + +addUnitAlias('dayOfYear', 'DDD'); + +// PRIORITY +addUnitPriority('dayOfYear', 4); + +// PARSING + +addRegexToken('DDD', match1to3); +addRegexToken('DDDD', match3); +addParseToken(['DDD', 'DDDD'], function (input, array, config) { + config._dayOfYear = toInt(input); +}); + +// HELPERS + +// MOMENTS + +function getSetDayOfYear (input) { + var dayOfYear = Math.round((this.clone().startOf('day') - this.clone().startOf('year')) / 864e5) + 1; + return input == null ? dayOfYear : this.add((input - dayOfYear), 'd'); +} + +// FORMATTING + +addFormatToken('m', ['mm', 2], 0, 'minute'); + +// ALIASES + +addUnitAlias('minute', 'm'); + +// PRIORITY + +addUnitPriority('minute', 14); + +// PARSING + +addRegexToken('m', match1to2); +addRegexToken('mm', match1to2, match2); +addParseToken(['m', 'mm'], MINUTE); + +// MOMENTS + +var getSetMinute = makeGetSet('Minutes', false); + +// FORMATTING + +addFormatToken('s', ['ss', 2], 0, 'second'); + +// ALIASES + +addUnitAlias('second', 's'); + +// PRIORITY + +addUnitPriority('second', 15); + +// PARSING + +addRegexToken('s', match1to2); +addRegexToken('ss', match1to2, match2); +addParseToken(['s', 'ss'], SECOND); + +// MOMENTS + +var getSetSecond = makeGetSet('Seconds', false); + +// FORMATTING + +addFormatToken('S', 0, 0, function () { + return ~~(this.millisecond() / 100); +}); + +addFormatToken(0, ['SS', 2], 0, function () { + return ~~(this.millisecond() / 10); +}); + +addFormatToken(0, ['SSS', 3], 0, 'millisecond'); +addFormatToken(0, ['SSSS', 4], 0, function () { + return this.millisecond() * 10; +}); +addFormatToken(0, ['SSSSS', 5], 0, function () { + return this.millisecond() * 100; +}); +addFormatToken(0, ['SSSSSS', 6], 0, function () { + return this.millisecond() * 1000; +}); +addFormatToken(0, ['SSSSSSS', 7], 0, function () { + return this.millisecond() * 10000; +}); +addFormatToken(0, ['SSSSSSSS', 8], 0, function () { + return this.millisecond() * 100000; +}); +addFormatToken(0, ['SSSSSSSSS', 9], 0, function () { + return this.millisecond() * 1000000; +}); + + +// ALIASES + +addUnitAlias('millisecond', 'ms'); + +// PRIORITY + +addUnitPriority('millisecond', 16); + +// PARSING + +addRegexToken('S', match1to3, match1); +addRegexToken('SS', match1to3, match2); +addRegexToken('SSS', match1to3, match3); + +var token; +for (token = 'SSSS'; token.length <= 9; token += 'S') { + addRegexToken(token, matchUnsigned); +} + +function parseMs(input, array) { + array[MILLISECOND] = toInt(('0.' + input) * 1000); +} + +for (token = 'S'; token.length <= 9; token += 'S') { + addParseToken(token, parseMs); +} +// MOMENTS + +var getSetMillisecond = makeGetSet('Milliseconds', false); + +// FORMATTING + +addFormatToken('z', 0, 0, 'zoneAbbr'); +addFormatToken('zz', 0, 0, 'zoneName'); + +// MOMENTS + +function getZoneAbbr () { + return this._isUTC ? 'UTC' : ''; +} + +function getZoneName () { + return this._isUTC ? 'Coordinated Universal Time' : ''; +} + +var proto = Moment.prototype; + +proto.add = add; +proto.calendar = calendar$1; +proto.clone = clone; +proto.diff = diff; +proto.endOf = endOf; +proto.format = format; +proto.from = from; +proto.fromNow = fromNow; +proto.to = to; +proto.toNow = toNow; +proto.get = stringGet; +proto.invalidAt = invalidAt; +proto.isAfter = isAfter; +proto.isBefore = isBefore; +proto.isBetween = isBetween; +proto.isSame = isSame; +proto.isSameOrAfter = isSameOrAfter; +proto.isSameOrBefore = isSameOrBefore; +proto.isValid = isValid$2; +proto.lang = lang; +proto.locale = locale; +proto.localeData = localeData; +proto.max = prototypeMax; +proto.min = prototypeMin; +proto.parsingFlags = parsingFlags; +proto.set = stringSet; +proto.startOf = startOf; +proto.subtract = subtract; +proto.toArray = toArray; +proto.toObject = toObject; +proto.toDate = toDate; +proto.toISOString = toISOString; +proto.inspect = inspect; +proto.toJSON = toJSON; +proto.toString = toString; +proto.unix = unix; +proto.valueOf = valueOf; +proto.creationData = creationData; + +// Year +proto.year = getSetYear; +proto.isLeapYear = getIsLeapYear; + +// Week Year +proto.weekYear = getSetWeekYear; +proto.isoWeekYear = getSetISOWeekYear; + +// Quarter +proto.quarter = proto.quarters = getSetQuarter; + +// Month +proto.month = getSetMonth; +proto.daysInMonth = getDaysInMonth; + +// Week +proto.week = proto.weeks = getSetWeek; +proto.isoWeek = proto.isoWeeks = getSetISOWeek; +proto.weeksInYear = getWeeksInYear; +proto.isoWeeksInYear = getISOWeeksInYear; + +// Day +proto.date = getSetDayOfMonth; +proto.day = proto.days = getSetDayOfWeek; +proto.weekday = getSetLocaleDayOfWeek; +proto.isoWeekday = getSetISODayOfWeek; +proto.dayOfYear = getSetDayOfYear; + +// Hour +proto.hour = proto.hours = getSetHour; + +// Minute +proto.minute = proto.minutes = getSetMinute; + +// Second +proto.second = proto.seconds = getSetSecond; + +// Millisecond +proto.millisecond = proto.milliseconds = getSetMillisecond; + +// Offset +proto.utcOffset = getSetOffset; +proto.utc = setOffsetToUTC; +proto.local = setOffsetToLocal; +proto.parseZone = setOffsetToParsedOffset; +proto.hasAlignedHourOffset = hasAlignedHourOffset; +proto.isDST = isDaylightSavingTime; +proto.isLocal = isLocal; +proto.isUtcOffset = isUtcOffset; +proto.isUtc = isUtc; +proto.isUTC = isUtc; + +// Timezone +proto.zoneAbbr = getZoneAbbr; +proto.zoneName = getZoneName; + +// Deprecations +proto.dates = deprecate('dates accessor is deprecated. Use date instead.', getSetDayOfMonth); +proto.months = deprecate('months accessor is deprecated. Use month instead', getSetMonth); +proto.years = deprecate('years accessor is deprecated. Use year instead', getSetYear); +proto.zone = deprecate('moment().zone is deprecated, use moment().utcOffset instead. http://momentjs.com/guides/#/warnings/zone/', getSetZone); +proto.isDSTShifted = deprecate('isDSTShifted is deprecated. See http://momentjs.com/guides/#/warnings/dst-shifted/ for more information', isDaylightSavingTimeShifted); + +function createUnix (input) { + return createLocal(input * 1000); +} + +function createInZone () { + return createLocal.apply(null, arguments).parseZone(); +} + +function preParsePostFormat (string) { + return string; +} + +var proto$1 = Locale.prototype; + +proto$1.calendar = calendar; +proto$1.longDateFormat = longDateFormat; +proto$1.invalidDate = invalidDate; +proto$1.ordinal = ordinal; +proto$1.preparse = preParsePostFormat; +proto$1.postformat = preParsePostFormat; +proto$1.relativeTime = relativeTime; +proto$1.pastFuture = pastFuture; +proto$1.set = set; + +// Month +proto$1.months = localeMonths; +proto$1.monthsShort = localeMonthsShort; +proto$1.monthsParse = localeMonthsParse; +proto$1.monthsRegex = monthsRegex; +proto$1.monthsShortRegex = monthsShortRegex; + +// Week +proto$1.week = localeWeek; +proto$1.firstDayOfYear = localeFirstDayOfYear; +proto$1.firstDayOfWeek = localeFirstDayOfWeek; + +// Day of Week +proto$1.weekdays = localeWeekdays; +proto$1.weekdaysMin = localeWeekdaysMin; +proto$1.weekdaysShort = localeWeekdaysShort; +proto$1.weekdaysParse = localeWeekdaysParse; + +proto$1.weekdaysRegex = weekdaysRegex; +proto$1.weekdaysShortRegex = weekdaysShortRegex; +proto$1.weekdaysMinRegex = weekdaysMinRegex; + +// Hours +proto$1.isPM = localeIsPM; +proto$1.meridiem = localeMeridiem; + +function get$1 (format, index, field, setter) { + var locale = getLocale(); + var utc = createUTC().set(setter, index); + return locale[field](utc, format); +} + +function listMonthsImpl (format, index, field) { + if (isNumber(format)) { + index = format; + format = undefined; + } + + format = format || ''; + + if (index != null) { + return get$1(format, index, field, 'month'); + } + + var i; + var out = []; + for (i = 0; i < 12; i++) { + out[i] = get$1(format, i, field, 'month'); + } + return out; +} + +// () +// (5) +// (fmt, 5) +// (fmt) +// (true) +// (true, 5) +// (true, fmt, 5) +// (true, fmt) +function listWeekdaysImpl (localeSorted, format, index, field) { + if (typeof localeSorted === 'boolean') { + if (isNumber(format)) { + index = format; + format = undefined; + } + + format = format || ''; + } else { + format = localeSorted; + index = format; + localeSorted = false; + + if (isNumber(format)) { + index = format; + format = undefined; + } + + format = format || ''; + } + + var locale = getLocale(), + shift = localeSorted ? locale._week.dow : 0; + + if (index != null) { + return get$1(format, (index + shift) % 7, field, 'day'); + } + + var i; + var out = []; + for (i = 0; i < 7; i++) { + out[i] = get$1(format, (i + shift) % 7, field, 'day'); + } + return out; +} + +function listMonths (format, index) { + return listMonthsImpl(format, index, 'months'); +} + +function listMonthsShort (format, index) { + return listMonthsImpl(format, index, 'monthsShort'); +} + +function listWeekdays (localeSorted, format, index) { + return listWeekdaysImpl(localeSorted, format, index, 'weekdays'); +} + +function listWeekdaysShort (localeSorted, format, index) { + return listWeekdaysImpl(localeSorted, format, index, 'weekdaysShort'); +} + +function listWeekdaysMin (localeSorted, format, index) { + return listWeekdaysImpl(localeSorted, format, index, 'weekdaysMin'); +} + +getSetGlobalLocale('en', { + dayOfMonthOrdinalParse: /\d{1,2}(th|st|nd|rd)/, + ordinal : function (number) { + var b = number % 10, + output = (toInt(number % 100 / 10) === 1) ? 'th' : + (b === 1) ? 'st' : + (b === 2) ? 'nd' : + (b === 3) ? 'rd' : 'th'; + return number + output; + } +}); + +// Side effect imports +hooks.lang = deprecate('moment.lang is deprecated. Use moment.locale instead.', getSetGlobalLocale); +hooks.langData = deprecate('moment.langData is deprecated. Use moment.localeData instead.', getLocale); + +var mathAbs = Math.abs; + +function abs () { + var data = this._data; + + this._milliseconds = mathAbs(this._milliseconds); + this._days = mathAbs(this._days); + this._months = mathAbs(this._months); + + data.milliseconds = mathAbs(data.milliseconds); + data.seconds = mathAbs(data.seconds); + data.minutes = mathAbs(data.minutes); + data.hours = mathAbs(data.hours); + data.months = mathAbs(data.months); + data.years = mathAbs(data.years); + + return this; +} + +function addSubtract$1 (duration, input, value, direction) { + var other = createDuration(input, value); + + duration._milliseconds += direction * other._milliseconds; + duration._days += direction * other._days; + duration._months += direction * other._months; + + return duration._bubble(); +} + +// supports only 2.0-style add(1, 's') or add(duration) +function add$1 (input, value) { + return addSubtract$1(this, input, value, 1); +} + +// supports only 2.0-style subtract(1, 's') or subtract(duration) +function subtract$1 (input, value) { + return addSubtract$1(this, input, value, -1); +} + +function absCeil (number) { + if (number < 0) { + return Math.floor(number); + } else { + return Math.ceil(number); + } +} + +function bubble () { + var milliseconds = this._milliseconds; + var days = this._days; + var months = this._months; + var data = this._data; + var seconds, minutes, hours, years, monthsFromDays; + + // if we have a mix of positive and negative values, bubble down first + // check: https://github.com/moment/moment/issues/2166 + if (!((milliseconds >= 0 && days >= 0 && months >= 0) || + (milliseconds <= 0 && days <= 0 && months <= 0))) { + milliseconds += absCeil(monthsToDays(months) + days) * 864e5; + days = 0; + months = 0; + } + + // The following code bubbles up values, see the tests for + // examples of what that means. + data.milliseconds = milliseconds % 1000; + + seconds = absFloor(milliseconds / 1000); + data.seconds = seconds % 60; + + minutes = absFloor(seconds / 60); + data.minutes = minutes % 60; + + hours = absFloor(minutes / 60); + data.hours = hours % 24; + + days += absFloor(hours / 24); + + // convert days to months + monthsFromDays = absFloor(daysToMonths(days)); + months += monthsFromDays; + days -= absCeil(monthsToDays(monthsFromDays)); + + // 12 months -> 1 year + years = absFloor(months / 12); + months %= 12; + + data.days = days; + data.months = months; + data.years = years; + + return this; +} + +function daysToMonths (days) { + // 400 years have 146097 days (taking into account leap year rules) + // 400 years have 12 months === 4800 + return days * 4800 / 146097; +} + +function monthsToDays (months) { + // the reverse of daysToMonths + return months * 146097 / 4800; +} + +function as (units) { + if (!this.isValid()) { + return NaN; + } + var days; + var months; + var milliseconds = this._milliseconds; + + units = normalizeUnits(units); + + if (units === 'month' || units === 'year') { + days = this._days + milliseconds / 864e5; + months = this._months + daysToMonths(days); + return units === 'month' ? months : months / 12; + } else { + // handle milliseconds separately because of floating point math errors (issue #1867) + days = this._days + Math.round(monthsToDays(this._months)); + switch (units) { + case 'week' : return days / 7 + milliseconds / 6048e5; + case 'day' : return days + milliseconds / 864e5; + case 'hour' : return days * 24 + milliseconds / 36e5; + case 'minute' : return days * 1440 + milliseconds / 6e4; + case 'second' : return days * 86400 + milliseconds / 1000; + // Math.floor prevents floating point math errors here + case 'millisecond': return Math.floor(days * 864e5) + milliseconds; + default: throw new Error('Unknown unit ' + units); + } + } +} + +// TODO: Use this.as('ms')? +function valueOf$1 () { + if (!this.isValid()) { + return NaN; + } + return ( + this._milliseconds + + this._days * 864e5 + + (this._months % 12) * 2592e6 + + toInt(this._months / 12) * 31536e6 + ); +} + +function makeAs (alias) { + return function () { + return this.as(alias); + }; +} + +var asMilliseconds = makeAs('ms'); +var asSeconds = makeAs('s'); +var asMinutes = makeAs('m'); +var asHours = makeAs('h'); +var asDays = makeAs('d'); +var asWeeks = makeAs('w'); +var asMonths = makeAs('M'); +var asYears = makeAs('y'); + +function clone$1 () { + return createDuration(this); +} + +function get$2 (units) { + units = normalizeUnits(units); + return this.isValid() ? this[units + 's']() : NaN; +} + +function makeGetter(name) { + return function () { + return this.isValid() ? this._data[name] : NaN; + }; +} + +var milliseconds = makeGetter('milliseconds'); +var seconds = makeGetter('seconds'); +var minutes = makeGetter('minutes'); +var hours = makeGetter('hours'); +var days = makeGetter('days'); +var months = makeGetter('months'); +var years = makeGetter('years'); + +function weeks () { + return absFloor(this.days() / 7); +} + +var round = Math.round; +var thresholds = { + ss: 44, // a few seconds to seconds + s : 45, // seconds to minute + m : 45, // minutes to hour + h : 22, // hours to day + d : 26, // days to month + M : 11 // months to year +}; + +// helper function for moment.fn.from, moment.fn.fromNow, and moment.duration.fn.humanize +function substituteTimeAgo(string, number, withoutSuffix, isFuture, locale) { + return locale.relativeTime(number || 1, !!withoutSuffix, string, isFuture); +} + +function relativeTime$1 (posNegDuration, withoutSuffix, locale) { + var duration = createDuration(posNegDuration).abs(); + var seconds = round(duration.as('s')); + var minutes = round(duration.as('m')); + var hours = round(duration.as('h')); + var days = round(duration.as('d')); + var months = round(duration.as('M')); + var years = round(duration.as('y')); + + var a = seconds <= thresholds.ss && ['s', seconds] || + seconds < thresholds.s && ['ss', seconds] || + minutes <= 1 && ['m'] || + minutes < thresholds.m && ['mm', minutes] || + hours <= 1 && ['h'] || + hours < thresholds.h && ['hh', hours] || + days <= 1 && ['d'] || + days < thresholds.d && ['dd', days] || + months <= 1 && ['M'] || + months < thresholds.M && ['MM', months] || + years <= 1 && ['y'] || ['yy', years]; + + a[2] = withoutSuffix; + a[3] = +posNegDuration > 0; + a[4] = locale; + return substituteTimeAgo.apply(null, a); +} + +// This function allows you to set the rounding function for relative time strings +function getSetRelativeTimeRounding (roundingFunction) { + if (roundingFunction === undefined) { + return round; + } + if (typeof(roundingFunction) === 'function') { + round = roundingFunction; + return true; + } + return false; +} + +// This function allows you to set a threshold for relative time strings +function getSetRelativeTimeThreshold (threshold, limit) { + if (thresholds[threshold] === undefined) { + return false; + } + if (limit === undefined) { + return thresholds[threshold]; + } + thresholds[threshold] = limit; + if (threshold === 's') { + thresholds.ss = limit - 1; + } + return true; +} + +function humanize (withSuffix) { + if (!this.isValid()) { + return this.localeData().invalidDate(); + } + + var locale = this.localeData(); + var output = relativeTime$1(this, !withSuffix, locale); + + if (withSuffix) { + output = locale.pastFuture(+this, output); + } + + return locale.postformat(output); +} + +var abs$1 = Math.abs; + +function sign(x) { + return ((x > 0) - (x < 0)) || +x; +} + +function toISOString$1() { + // for ISO strings we do not use the normal bubbling rules: + // * milliseconds bubble up until they become hours + // * days do not bubble at all + // * months bubble up until they become years + // This is because there is no context-free conversion between hours and days + // (think of clock changes) + // and also not between days and months (28-31 days per month) + if (!this.isValid()) { + return this.localeData().invalidDate(); + } + + var seconds = abs$1(this._milliseconds) / 1000; + var days = abs$1(this._days); + var months = abs$1(this._months); + var minutes, hours, years; + + // 3600 seconds -> 60 minutes -> 1 hour + minutes = absFloor(seconds / 60); + hours = absFloor(minutes / 60); + seconds %= 60; + minutes %= 60; + + // 12 months -> 1 year + years = absFloor(months / 12); + months %= 12; + + + // inspired by https://github.com/dordille/moment-isoduration/blob/master/moment.isoduration.js + var Y = years; + var M = months; + var D = days; + var h = hours; + var m = minutes; + var s = seconds ? seconds.toFixed(3).replace(/\.?0+$/, '') : ''; + var total = this.asSeconds(); + + if (!total) { + // this is the same as C#'s (Noda) and python (isodate)... + // but not other JS (goog.date) + return 'P0D'; + } + + var totalSign = total < 0 ? '-' : ''; + var ymSign = sign(this._months) !== sign(total) ? '-' : ''; + var daysSign = sign(this._days) !== sign(total) ? '-' : ''; + var hmsSign = sign(this._milliseconds) !== sign(total) ? '-' : ''; + + return totalSign + 'P' + + (Y ? ymSign + Y + 'Y' : '') + + (M ? ymSign + M + 'M' : '') + + (D ? daysSign + D + 'D' : '') + + ((h || m || s) ? 'T' : '') + + (h ? hmsSign + h + 'H' : '') + + (m ? hmsSign + m + 'M' : '') + + (s ? hmsSign + s + 'S' : ''); +} + +var proto$2 = Duration.prototype; + +proto$2.isValid = isValid$1; +proto$2.abs = abs; +proto$2.add = add$1; +proto$2.subtract = subtract$1; +proto$2.as = as; +proto$2.asMilliseconds = asMilliseconds; +proto$2.asSeconds = asSeconds; +proto$2.asMinutes = asMinutes; +proto$2.asHours = asHours; +proto$2.asDays = asDays; +proto$2.asWeeks = asWeeks; +proto$2.asMonths = asMonths; +proto$2.asYears = asYears; +proto$2.valueOf = valueOf$1; +proto$2._bubble = bubble; +proto$2.clone = clone$1; +proto$2.get = get$2; +proto$2.milliseconds = milliseconds; +proto$2.seconds = seconds; +proto$2.minutes = minutes; +proto$2.hours = hours; +proto$2.days = days; +proto$2.weeks = weeks; +proto$2.months = months; +proto$2.years = years; +proto$2.humanize = humanize; +proto$2.toISOString = toISOString$1; +proto$2.toString = toISOString$1; +proto$2.toJSON = toISOString$1; +proto$2.locale = locale; +proto$2.localeData = localeData; + +// Deprecations +proto$2.toIsoString = deprecate('toIsoString() is deprecated. Please use toISOString() instead (notice the capitals)', toISOString$1); +proto$2.lang = lang; + +// Side effect imports + +// FORMATTING + +addFormatToken('X', 0, 0, 'unix'); +addFormatToken('x', 0, 0, 'valueOf'); + +// PARSING + +addRegexToken('x', matchSigned); +addRegexToken('X', matchTimestamp); +addParseToken('X', function (input, array, config) { + config._d = new Date(parseFloat(input, 10) * 1000); +}); +addParseToken('x', function (input, array, config) { + config._d = new Date(toInt(input)); +}); + +// Side effect imports + + +hooks.version = '2.20.1'; + +setHookCallback(createLocal); + +hooks.fn = proto; +hooks.min = min; +hooks.max = max; +hooks.now = now; +hooks.utc = createUTC; +hooks.unix = createUnix; +hooks.months = listMonths; +hooks.isDate = isDate; +hooks.locale = getSetGlobalLocale; +hooks.invalid = createInvalid; +hooks.duration = createDuration; +hooks.isMoment = isMoment; +hooks.weekdays = listWeekdays; +hooks.parseZone = createInZone; +hooks.localeData = getLocale; +hooks.isDuration = isDuration; +hooks.monthsShort = listMonthsShort; +hooks.weekdaysMin = listWeekdaysMin; +hooks.defineLocale = defineLocale; +hooks.updateLocale = updateLocale; +hooks.locales = listLocales; +hooks.weekdaysShort = listWeekdaysShort; +hooks.normalizeUnits = normalizeUnits; +hooks.relativeTimeRounding = getSetRelativeTimeRounding; +hooks.relativeTimeThreshold = getSetRelativeTimeThreshold; +hooks.calendarFormat = getCalendarFormat; +hooks.prototype = proto; + +// currently HTML5 input type only supports 24-hour formats +hooks.HTML5_FMT = { + DATETIME_LOCAL: 'YYYY-MM-DDTHH:mm', // + DATETIME_LOCAL_SECONDS: 'YYYY-MM-DDTHH:mm:ss', // + DATETIME_LOCAL_MS: 'YYYY-MM-DDTHH:mm:ss.SSS', // + DATE: 'YYYY-MM-DD', // + TIME: 'HH:mm', // + TIME_SECONDS: 'HH:mm:ss', // + TIME_MS: 'HH:mm:ss.SSS', // + WEEK: 'YYYY-[W]WW', // + MONTH: 'YYYY-MM' // +}; + +return hooks; + +}))); + +/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(3)(module))) + +/***/ }), +/* 1 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.setLogLevel = exports.logger = exports.LEVELS = undefined; + +var _moment = __webpack_require__(0); + +var _moment2 = _interopRequireDefault(_moment); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +var LEVELS = exports.LEVELS = { + debug: 1, + info: 2, + warn: 3, + error: 4, + fatal: 5 +}; + +var logger = exports.logger = { + debug: function debug() {}, + info: function info() {}, + warn: function warn() {}, + error: function error() {}, + fatal: function fatal() {} +}; + +var setLogLevel = exports.setLogLevel = function setLogLevel(level) { + logger.debug = function () {}; + logger.info = function () {}; + logger.warn = function () {}; + logger.error = function () {}; + logger.fatal = function () {}; + if (level <= LEVELS.fatal) { + logger.fatal = console.log.bind(console, '\x1b[35m', format('FATAL')); + } + if (level <= LEVELS.error) { + logger.error = console.log.bind(console, '\x1b[31m', format('ERROR')); + } + if (level <= LEVELS.warn) { + logger.warn = console.log.bind(console, '\x1B[33m', format('WARN')); + } + if (level <= LEVELS.info) { + logger.info = console.log.bind(console, '\x1b[34m', format('INFO')); + } + if (level <= LEVELS.debug) { + logger.debug = console.log.bind(console, '\x1b[32m', format('DEBUG')); + } +}; + +var format = function format(level) { + var time = (0, _moment2.default)().format('HH:mm:ss.SSS'); + return time + ' : ' + level + ' : '; +}; + +/***/ }), +/* 2 */ +/***/ (function(module, exports) { + +/** + * Checks if `value` is classified as an `Array` object. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is an array, else `false`. + * @example + * + * _.isArray([1, 2, 3]); + * // => true + * + * _.isArray(document.body.children); + * // => false + * + * _.isArray('abc'); + * // => false + * + * _.isArray(_.noop); + * // => false + */ +var isArray = Array.isArray; + +module.exports = isArray; + + +/***/ }), +/* 3 */ +/***/ (function(module, exports) { + +module.exports = function(module) { + if(!module.webpackPolyfill) { + module.deprecate = function() {}; + module.paths = []; + // module.parent = undefined by default + if(!module.children) module.children = []; + Object.defineProperty(module, "loaded", { + enumerable: true, + get: function() { + return module.l; + } + }); + Object.defineProperty(module, "id", { + enumerable: true, + get: function() { + return module.i; + } + }); + module.webpackPolyfill = 1; + } + return module; +}; + + +/***/ }), +/* 4 */ +/***/ (function(module, exports, __webpack_require__) { + +/* global window */ + +var lodash; + +if (true) { + try { + lodash = __webpack_require__(19); + } catch (e) {} +} + +if (!lodash) { + lodash = window._; +} + +module.exports = lodash; + + +/***/ }), +/* 5 */ +/***/ (function(module, exports, __webpack_require__) { + +var freeGlobal = __webpack_require__(181); + +/** Detect free variable `self`. */ +var freeSelf = typeof self == 'object' && self && self.Object === Object && self; + +/** Used as a reference to the global object. */ +var root = freeGlobal || freeSelf || Function('return this')(); + +module.exports = root; + + +/***/ }), +/* 6 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; + +var _d = __webpack_require__(159); + +var _d2 = _interopRequireDefault(_d); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +/* + D3 Text Wrap + By Vijith Assar + http://www.vijithassar.com + http://www.github.com/vijithassar + @vijithassar + + Detailed instructions at http://www.github.com/vijithassar/d3textwrap + */ + +(function () { + // set this variable to a string value to always force a particular + // wrap method for development purposes, for example to check tspan + // rendering using a foreignobject-enabled browser. set to 'tspan' to + // use tspans and 'foreignobject' to use foreignobject + var forceWrapMethod = false; // by default no wrap method is forced + forceWrapMethod = 'tspans'; // uncomment this statement to force tspans + // force_wrap_method = 'foreignobjects'; // uncomment this statement to force foreignobjects + + // exit immediately if something in this location + // has already been defined; the plugin will defer to whatever + // else you're doing in your code + if (_d2.default.selection.prototype.textwrap) { + return false; + } + + // double check the force_wrap_method flag + // and reset if someone screwed up the above + // settings + if (typeof forceWrapMethod === 'undefined') { + forceWrapMethod = false; + } + + // create the plugin method twice, both for regular use + // and again for use inside the enter() selection + _d2.default.selection.prototype.textwrap = _d2.default.selection.enter.prototype.textwrap = function (bounds, padding) { + // default value of padding is zero if it's undefined + padding = parseInt(padding) || 0; + + // save callee into a variable so we can continue to refer to it + // as the function scope changes + var selection = this; + + // create a variable to store desired return values in + var returnValue; + + // extract wrap boundaries from any d3-selected rect and return them + // in a format that matches the simpler object argument option + var extractBounds = function extractBounds(bounds) { + // discard the nested array wrappers added by d3 + var boundingRect = bounds[0][0]; + // sanitize the svg element name so we can test against it + var elementType = boundingRect.tagName.toString(); + // if it's not a rect, exit + if (elementType !== 'rect') { + return false; + // if it's a rect, proceed to extracting the position attributes + } else { + var boundsExtracted = {}; + boundsExtracted.x = _d2.default.select(boundingRect).attr('x') || 0; + boundsExtracted.y = _d2.default.select(boundingRect).attr('y') || 0; + boundsExtracted.width = _d2.default.select(boundingRect).attr('width') || 0; + boundsExtracted.height = _d2.default.select(boundingRect).attr('height') || 0; + // also pass along the getter function + boundsExtracted.attr = bounds.attr; + } + return boundsExtracted; + }; + + // double check the input argument for the wrapping + // boundaries to make sure it actually contains all + // the information we'll need in order to wrap successfully + var verifyBounds = function verifyBounds(bounds) { + // quickly add a simple getter method so you can use either + // bounds.x or bounds.attr('x') as your notation, + // the latter being a common convention among D3 + // developers + if (!bounds.attr) { + bounds.attr = function (property) { + if (this[property]) { + return this[property]; + } + }; + } + // if it's an associative array, make sure it has all the + // necessary properties represented directly + if ((typeof bounds === 'undefined' ? 'undefined' : _typeof(bounds)) === 'object' && typeof bounds.x !== 'undefined' && typeof bounds.y !== 'undefined' && typeof bounds.width !== 'undefined' && typeof bounds.height !== 'undefined' + // if that's the case, then the bounds are fine + ) { + // return the lightly modified bounds + return bounds; + // if it's a numerically indexed array, assume it's a + // d3-selected rect and try to extract the positions + } else if ( + // first try to make sure it's an array using Array.isArray + typeof Array.isArray === 'function' && Array.isArray(bounds) || + // but since Array.isArray isn't always supported, fall + // back to casting to the object to string when it's not + Object.prototype.toString.call(bounds) === '[object Array]') { + // once you're sure it's an array, extract the boundaries + // from the rect + var extractedBounds = extractBounds(bounds); + return extractedBounds; + } else { + // but if the bounds are neither an object nor a numerical + // array, then the bounds argument is invalid and you'll + // need to fix it + return false; + } + }; + + var applyPadding = function applyPadding(bounds, padding) { + var paddedBounds = bounds; + if (padding !== 0) { + paddedBounds.x = parseInt(paddedBounds.x) + padding; + paddedBounds.y = parseInt(paddedBounds.y) + padding; + paddedBounds.width -= padding * 2; + paddedBounds.height -= padding * 2; + } + return paddedBounds; + }; + + // verify bounds + var verifiedBounds = verifyBounds(bounds); + + // modify bounds if a padding value is provided + if (padding) { + verifiedBounds = applyPadding(verifiedBounds, padding); + } + + // check that we have the necessary conditions for this function to operate properly + if ( + // selection it's operating on cannot be not empty + selection.length === 0 || + // d3 must be available + !_d2.default || + // desired wrapping bounds must be provided as an input argument + !bounds || + // input bounds must validate + !verifiedBounds) { + // try to return the calling selection if possible + // so as not to interfere with methods downstream in the + // chain + if (selection) { + return selection; + // if all else fails, just return false. if you hit this point then you're + // almost certainly trying to call the textwrap() method on something that + // doesn't make sense! + } else { + return false; + } + // if we've validated everything then we can finally proceed + // to the meat of this operation + } else { + // reassign the verified bounds as the set we want + // to work with from here on; this ensures that we're + // using the same data structure for our bounds regardless + // of whether the input argument was a simple object or + // a d3 selection + bounds = verifiedBounds; + + // wrap using html and foreignObjects if they are supported + var wrapWithForeignobjects = function wrapWithForeignobjects(item) { + // establish variables to quickly reference target nodes later + var parent = _d2.default.select(item[0].parentNode); + var textNode = parent.select('text'); + var styledLineHeight = textNode.style('line-height'); + // extract our desired content from the single text element + var textToWrap = textNode.text(); + // remove the text node and replace with a foreign object + textNode.remove(); + var foreignObject = parent.append('foreignObject'); + // add foreign object and set dimensions, position, etc + foreignObject.attr('requiredFeatures', 'http://www.w3.org/TR/SVG11/feature#Extensibility').attr('x', bounds.x).attr('y', bounds.y).attr('width', bounds.width).attr('height', bounds.height); + // insert an HTML div + var wrapDiv = foreignObject.append('xhtml:div') + // this class is currently hardcoded + // probably not necessary but easy to + // override using .classed() and for now + // it's nice to avoid a litany of input + // arguments + .attr('class', 'wrapped'); + // set div to same dimensions as foreign object + wrapDiv.style('height', bounds.height).style('width', bounds.width) + // insert text content + .html(textToWrap); + if (styledLineHeight) { + wrapDiv.style('line-height', styledLineHeight); + } + returnValue = parent.select('foreignObject'); + }; + + // wrap with tspans if foreignObject is undefined + var wrapWithTspans = function wrapWithTspans(item) { + // operate on the first text item in the selection + var textNode = item[0]; + var parent = textNode.parentNode; + var textNodeSelected = _d2.default.select(textNode); + // measure initial size of the text node as rendered + var textNodeHeight = textNode.getBBox().height; + var textNodeWidth = textNode.getBBox().width; + // figure out the line height, either from rendered height + // of the font or attached styling + var lineHeight; + var renderedLineHeight = textNodeHeight; + var styledLineHeight = textNodeSelected.style('line-height'); + if (styledLineHeight && parseInt(styledLineHeight)) { + lineHeight = parseInt(styledLineHeight.replace('px', '')); + } else { + lineHeight = renderedLineHeight; + } + // only fire the rest of this if the text content + // overflows the desired dimensions + if (textNodeWidth > bounds.width) { + // store whatever is inside the text node + // in a variable and then zero out the + // initial content; we'll reinsert in a moment + // using tspan elements. + var textToWrap = textNodeSelected.text(); + textNodeSelected.text(''); + if (textToWrap) { + // keep track of whether we are splitting by spaces + // so we know whether to reinsert those spaces later + var breakDelimiter; + // split at spaces to create an array of individual words + var textToWrapArray; + if (textToWrap.indexOf(' ') !== -1) { + breakDelimiter = ' '; + textToWrapArray = textToWrap.split(' '); + } else { + // if there are no spaces, figure out the split + // points by comparing rendered text width against + // bounds and translating that into character position + // cuts + breakDelimiter = ''; + var stringLength = textToWrap.length; + var numberOfSubstrings = Math.ceil(textNodeWidth / bounds.width); + var spliceInterval = Math.floor(stringLength / numberOfSubstrings); + if (!(spliceInterval * numberOfSubstrings >= stringLength)) { + numberOfSubstrings++; + } + textToWrapArray = []; + var substring; + var startPosition; + for (var i = 0; i < numberOfSubstrings; i++) { + startPosition = i * spliceInterval; + substring = textToWrap.substr(startPosition, spliceInterval); + textToWrapArray.push(substring); + } + } + + // new array where we'll store the words re-assembled into + // substrings that have been tested against the desired + // maximum wrapping width + var substrings = []; + // computed text length is arguably incorrectly reported for + // all tspans after the first one, in that they will include + // the width of previous separate tspans. to compensate we need + // to manually track the computed text length of all those + // previous tspans and substrings, and then use that to offset + // the miscalculation. this then gives us the actual correct + // position we want to use in rendering the text in the SVG. + var totalOffset = 0; + // object for storing the results of text length computations later + var temp = {}; + // loop through the words and test the computed text length + // of the string against the maximum desired wrapping width + for (i = 0; i < textToWrapArray.length; i++) { + var word = textToWrapArray[i]; + var previousString = textNodeSelected.text(); + var previousWidth = textNode.getComputedTextLength(); + // initialize the current word as the first word + // or append to the previous string if one exists + var newstring; + if (previousString) { + newstring = previousString + breakDelimiter + word; + } else { + newstring = word; + } + // add the newest substring back to the text node and + // measure the length + textNodeSelected.text(newstring); + var newWidth = textNode.getComputedTextLength(); + // adjust the length by the offset we've tracked + // due to the misreported length discussed above + + // if our latest version of the string is too + // big for the bounds, use the previous + // version of the string (without the newest word + // added) and use the latest word to restart the + // process with a new tspan + if (newWidth > bounds.width) { + if (previousString && previousString !== '') { + totalOffset = totalOffset + previousWidth; + temp = { string: previousString, width: previousWidth, offset: totalOffset }; + substrings.push(temp); + textNodeSelected.text(''); + textNodeSelected.text(word); + // Handle case where there is just one more word to be wrapped + if (i === textToWrapArray.length - 1) { + newstring = word; + textNodeSelected.text(newstring); + newWidth = textNode.getComputedTextLength(); + } + } + } + // if we're up to the last word in the array, + // get the computed length as is without + // appending anything further to it + if (i === textToWrapArray.length - 1) { + textNodeSelected.text(''); + var finalString = newstring; + if (finalString && finalString !== '') { + if (newWidth - totalOffset > 0) { + newWidth = newWidth - totalOffset; + } + temp = { string: finalString, width: newWidth, offset: totalOffset }; + substrings.push(temp); + } + } + } + + // append each substring as a tspan + var currentTspan; + // var tspanCount + // double check that the text content has been removed + // before we start appending tspans + textNodeSelected.text(''); + for (i = 0; i < substrings.length; i++) { + substring = substrings[i].string; + // only append if we're sure it won't make the tspans + // overflow the bounds. + if (i * lineHeight < bounds.height - lineHeight * 1.5) { + currentTspan = textNodeSelected.append('tspan').text(substring); + // vertical shift to all tspans after the first one + currentTspan.attr('dy', function (d) { + if (i > 0) { + return lineHeight; + } + }); + // shift left from default position, which + // is probably based on the full length of the + // text string until we make this adjustment + currentTspan.attr('x', function () { + var xOffset = bounds.x; + if (padding) { + xOffset += padding; + } + return xOffset; + }); + } + } + } + } + // position the overall text node, whether wrapped or not + textNodeSelected.attr('y', function () { + var yOffset = bounds.y; + // shift by line-height to move the baseline into + // the bounds – otherwise the text baseline would be + // at the top of the bounds + if (lineHeight) { + yOffset += lineHeight; + } + // shift by padding, if it's there + if (padding) { + yOffset += padding; + } + return yOffset; + }); + // shift to the right by the padding value + textNodeSelected.attr('x', function () { + var xOffset = bounds.x; + if (padding) { + xOffset += padding; + } + return xOffset; + }); + + // assign our modified text node with tspans + // to the return value + returnValue = _d2.default.select(parent).selectAll('text'); + }; + + // variable used to hold the functions that let us + // switch between the wrap methods + var wrapMethod; + + // if a wrap method if being forced, assign that + // function + if (forceWrapMethod) { + if (forceWrapMethod === 'foreignobjects') { + wrapMethod = wrapWithForeignobjects; + } else if (forceWrapMethod === 'tspans') { + wrapMethod = wrapWithTspans; + } + } + + // if no wrap method is being forced, then instead + // test for browser support of foreignobject and + // use whichever wrap method makes sense accordingly + if (!forceWrapMethod) { + if (typeof SVGForeignObjectElement !== 'undefined') { + wrapMethod = wrapWithForeignobjects; + } else { + wrapMethod = wrapWithTspans; + } + } + + // run the desired wrap function for each item + // in the d3 selection that called .textwrap() + for (var i = 0; i < selection.length; i++) { + var item = selection[i]; + wrapMethod(item); + } + + // return the modified nodes so we can chain other + // methods to them. + return returnValue; + } + }; +})(); + +exports.default = _d2.default; + +/***/ }), +/* 7 */ +/***/ (function(module, exports) { + +// shim for using process in browser +var process = module.exports = {}; + +// cached from whatever global is present so that test runners that stub it +// don't break things. But we need to wrap it in a try catch in case it is +// wrapped in strict mode code which doesn't define any globals. It's inside a +// function because try/catches deoptimize in certain engines. + +var cachedSetTimeout; +var cachedClearTimeout; + +function defaultSetTimout() { + throw new Error('setTimeout has not been defined'); +} +function defaultClearTimeout () { + throw new Error('clearTimeout has not been defined'); +} +(function () { + try { + if (typeof setTimeout === 'function') { + cachedSetTimeout = setTimeout; + } else { + cachedSetTimeout = defaultSetTimout; + } + } catch (e) { + cachedSetTimeout = defaultSetTimout; + } + try { + if (typeof clearTimeout === 'function') { + cachedClearTimeout = clearTimeout; + } else { + cachedClearTimeout = defaultClearTimeout; + } + } catch (e) { + cachedClearTimeout = defaultClearTimeout; + } +} ()) +function runTimeout(fun) { + if (cachedSetTimeout === setTimeout) { + //normal enviroments in sane situations + return setTimeout(fun, 0); + } + // if setTimeout wasn't available but was latter defined + if ((cachedSetTimeout === defaultSetTimout || !cachedSetTimeout) && setTimeout) { + cachedSetTimeout = setTimeout; + return setTimeout(fun, 0); + } + try { + // when when somebody has screwed with setTimeout but no I.E. maddness + return cachedSetTimeout(fun, 0); + } catch(e){ + try { + // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally + return cachedSetTimeout.call(null, fun, 0); + } catch(e){ + // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error + return cachedSetTimeout.call(this, fun, 0); + } + } + + +} +function runClearTimeout(marker) { + if (cachedClearTimeout === clearTimeout) { + //normal enviroments in sane situations + return clearTimeout(marker); + } + // if clearTimeout wasn't available but was latter defined + if ((cachedClearTimeout === defaultClearTimeout || !cachedClearTimeout) && clearTimeout) { + cachedClearTimeout = clearTimeout; + return clearTimeout(marker); + } + try { + // when when somebody has screwed with setTimeout but no I.E. maddness + return cachedClearTimeout(marker); + } catch (e){ + try { + // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally + return cachedClearTimeout.call(null, marker); + } catch (e){ + // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error. + // Some versions of I.E. have different rules for clearTimeout vs setTimeout + return cachedClearTimeout.call(this, marker); + } + } + + + +} +var queue = []; +var draining = false; +var currentQueue; +var queueIndex = -1; + +function cleanUpNextTick() { + if (!draining || !currentQueue) { + return; + } + draining = false; + if (currentQueue.length) { + queue = currentQueue.concat(queue); + } else { + queueIndex = -1; + } + if (queue.length) { + drainQueue(); + } +} + +function drainQueue() { + if (draining) { + return; + } + var timeout = runTimeout(cleanUpNextTick); + draining = true; + + var len = queue.length; + while(len) { + currentQueue = queue; + queue = []; + while (++queueIndex < len) { + if (currentQueue) { + currentQueue[queueIndex].run(); + } + } + queueIndex = -1; + len = queue.length; + } + currentQueue = null; + draining = false; + runClearTimeout(timeout); +} + +process.nextTick = function (fun) { + var args = new Array(arguments.length - 1); + if (arguments.length > 1) { + for (var i = 1; i < arguments.length; i++) { + args[i - 1] = arguments[i]; + } + } + queue.push(new Item(fun, args)); + if (queue.length === 1 && !draining) { + runTimeout(drainQueue); + } +}; + +// v8 likes predictible objects +function Item(fun, array) { + this.fun = fun; + this.array = array; +} +Item.prototype.run = function () { + this.fun.apply(null, this.array); +}; +process.title = 'browser'; +process.browser = true; +process.env = {}; +process.argv = []; +process.version = ''; // empty string to avoid regexp issues +process.versions = {}; + +function noop() {} + +process.on = noop; +process.addListener = noop; +process.once = noop; +process.off = noop; +process.removeListener = noop; +process.removeAllListeners = noop; +process.emit = noop; +process.prependListener = noop; +process.prependOnceListener = noop; + +process.listeners = function (name) { return [] } + +process.binding = function (name) { + throw new Error('process.binding is not supported'); +}; + +process.cwd = function () { return '/' }; +process.chdir = function (dir) { + throw new Error('process.chdir is not supported'); +}; +process.umask = function() { return 0; }; + + +/***/ }), +/* 8 */ +/***/ (function(module, exports) { + + + +/***/ }), +/* 9 */ +/***/ (function(module, exports, __webpack_require__) { + +/* WEBPACK VAR INJECTION */(function(process) {// Copyright Joyent, Inc. and other Node contributors. +// +// 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. + +// resolves . and .. elements in a path array with directory names there +// must be no slashes, empty elements, or device names (c:\) in the array +// (so also no leading and trailing slashes - it does not distinguish +// relative and absolute paths) +function normalizeArray(parts, allowAboveRoot) { + // if the path tries to go above the root, `up` ends up > 0 + var up = 0; + for (var i = parts.length - 1; i >= 0; i--) { + var last = parts[i]; + if (last === '.') { + parts.splice(i, 1); + } else if (last === '..') { + parts.splice(i, 1); + up++; + } else if (up) { + parts.splice(i, 1); + up--; + } + } + + // if the path is allowed to go above the root, restore leading ..s + if (allowAboveRoot) { + for (; up--; up) { + parts.unshift('..'); + } + } + + return parts; +} + +// Split a filename into [root, dir, basename, ext], unix version +// 'root' is just a slash, or nothing. +var splitPathRe = + /^(\/?|)([\s\S]*?)((?:\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:[\/]*)$/; +var splitPath = function(filename) { + return splitPathRe.exec(filename).slice(1); +}; + +// path.resolve([from ...], to) +// posix version +exports.resolve = function() { + var resolvedPath = '', + resolvedAbsolute = false; + + for (var i = arguments.length - 1; i >= -1 && !resolvedAbsolute; i--) { + var path = (i >= 0) ? arguments[i] : process.cwd(); + + // Skip empty and invalid entries + if (typeof path !== 'string') { + throw new TypeError('Arguments to path.resolve must be strings'); + } else if (!path) { + continue; + } + + resolvedPath = path + '/' + resolvedPath; + resolvedAbsolute = path.charAt(0) === '/'; + } + + // At this point the path should be resolved to a full absolute path, but + // handle relative paths to be safe (might happen when process.cwd() fails) + + // Normalize the path + resolvedPath = normalizeArray(filter(resolvedPath.split('/'), function(p) { + return !!p; + }), !resolvedAbsolute).join('/'); + + return ((resolvedAbsolute ? '/' : '') + resolvedPath) || '.'; +}; + +// path.normalize(path) +// posix version +exports.normalize = function(path) { + var isAbsolute = exports.isAbsolute(path), + trailingSlash = substr(path, -1) === '/'; + + // Normalize the path + path = normalizeArray(filter(path.split('/'), function(p) { + return !!p; + }), !isAbsolute).join('/'); + + if (!path && !isAbsolute) { + path = '.'; + } + if (path && trailingSlash) { + path += '/'; + } + + return (isAbsolute ? '/' : '') + path; +}; + +// posix version +exports.isAbsolute = function(path) { + return path.charAt(0) === '/'; +}; + +// posix version +exports.join = function() { + var paths = Array.prototype.slice.call(arguments, 0); + return exports.normalize(filter(paths, function(p, index) { + if (typeof p !== 'string') { + throw new TypeError('Arguments to path.join must be strings'); + } + return p; + }).join('/')); +}; + + +// path.relative(from, to) +// posix version +exports.relative = function(from, to) { + from = exports.resolve(from).substr(1); + to = exports.resolve(to).substr(1); + + function trim(arr) { + var start = 0; + for (; start < arr.length; start++) { + if (arr[start] !== '') break; + } + + var end = arr.length - 1; + for (; end >= 0; end--) { + if (arr[end] !== '') break; + } + + if (start > end) return []; + return arr.slice(start, end - start + 1); + } + + var fromParts = trim(from.split('/')); + var toParts = trim(to.split('/')); + + var length = Math.min(fromParts.length, toParts.length); + var samePartsLength = length; + for (var i = 0; i < length; i++) { + if (fromParts[i] !== toParts[i]) { + samePartsLength = i; + break; + } + } + + var outputParts = []; + for (var i = samePartsLength; i < fromParts.length; i++) { + outputParts.push('..'); + } + + outputParts = outputParts.concat(toParts.slice(samePartsLength)); + + return outputParts.join('/'); +}; + +exports.sep = '/'; +exports.delimiter = ':'; + +exports.dirname = function(path) { + var result = splitPath(path), + root = result[0], + dir = result[1]; + + if (!root && !dir) { + // No dirname whatsoever + return '.'; + } + + if (dir) { + // It has a dirname, strip trailing slash + dir = dir.substr(0, dir.length - 1); + } + + return root + dir; +}; + + +exports.basename = function(path, ext) { + var f = splitPath(path)[2]; + // TODO: make this comparison case-insensitive on windows? + if (ext && f.substr(-1 * ext.length) === ext) { + f = f.substr(0, f.length - ext.length); + } + return f; +}; + + +exports.extname = function(path) { + return splitPath(path)[3]; +}; + +function filter (xs, f) { + if (xs.filter) return xs.filter(f); + var res = []; + for (var i = 0; i < xs.length; i++) { + if (f(xs[i], i, xs)) res.push(xs[i]); + } + return res; +} + +// String.prototype.substr - negative index don't work in IE8 +var substr = 'ab'.substr(-1) === 'b' + ? function (str, start, len) { return str.substr(start, len) } + : function (str, start, len) { + if (start < 0) start = str.length + start; + return str.substr(start, len); + } +; + +/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(7))) + +/***/ }), +/* 10 */ +/***/ (function(module, exports, __webpack_require__) { + +var baseIsNative = __webpack_require__(249), + getValue = __webpack_require__(252); + +/** + * Gets the native function at `key` of `object`. + * + * @private + * @param {Object} object The object to query. + * @param {string} key The key of the method to get. + * @returns {*} Returns the function if it's native, else `undefined`. + */ +function getNative(object, key) { + var value = getValue(object, key); + return baseIsNative(value) ? value : undefined; +} + +module.exports = getNative; + + +/***/ }), +/* 11 */ +/***/ (function(module, exports, __webpack_require__) { + +var Symbol = __webpack_require__(21), + getRawTag = __webpack_require__(235), + objectToString = __webpack_require__(236); + +/** `Object#toString` result references. */ +var nullTag = '[object Null]', + undefinedTag = '[object Undefined]'; + +/** Built-in value references. */ +var symToStringTag = Symbol ? Symbol.toStringTag : undefined; + +/** + * The base implementation of `getTag` without fallbacks for buggy environments. + * + * @private + * @param {*} value The value to query. + * @returns {string} Returns the `toStringTag`. + */ +function baseGetTag(value) { + if (value == null) { + return value === undefined ? undefinedTag : nullTag; + } + return (symToStringTag && symToStringTag in Object(value)) + ? getRawTag(value) + : objectToString(value); +} + +module.exports = baseGetTag; + + +/***/ }), +/* 12 */ +/***/ (function(module, exports) { + +/** + * Checks if `value` is object-like. A value is object-like if it's not `null` + * and has a `typeof` result of "object". + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is object-like, else `false`. + * @example + * + * _.isObjectLike({}); + * // => true + * + * _.isObjectLike([1, 2, 3]); + * // => true + * + * _.isObjectLike(_.noop); + * // => false + * + * _.isObjectLike(null); + * // => false + */ +function isObjectLike(value) { + return value != null && typeof value == 'object'; +} + +module.exports = isObjectLike; + + +/***/ }), +/* 13 */ +/***/ (function(module, exports, __webpack_require__) { + +var isFunction = __webpack_require__(186), + isLength = __webpack_require__(32); + +/** + * Checks if `value` is array-like. A value is considered array-like if it's + * not a function and has a `value.length` that's an integer greater than or + * equal to `0` and less than or equal to `Number.MAX_SAFE_INTEGER`. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is array-like, else `false`. + * @example + * + * _.isArrayLike([1, 2, 3]); + * // => true + * + * _.isArrayLike(document.body.children); + * // => true + * + * _.isArrayLike('abc'); + * // => true + * + * _.isArrayLike(_.noop); + * // => false + */ +function isArrayLike(value) { + return value != null && isLength(value.length) && !isFunction(value); +} + +module.exports = isArrayLike; + + +/***/ }), +/* 14 */ +/***/ (function(module, exports) { + +/** + * Checks if `value` is the + * [language type](http://www.ecma-international.org/ecma-262/7.0/#sec-ecmascript-language-types) + * of `Object`. (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`) + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is an object, else `false`. + * @example + * + * _.isObject({}); + * // => true + * + * _.isObject([1, 2, 3]); + * // => true + * + * _.isObject(_.noop); + * // => true + * + * _.isObject(null); + * // => false + */ +function isObject(value) { + var type = typeof value; + return value != null && (type == 'object' || type == 'function'); +} + +module.exports = isObject; + + +/***/ }), +/* 15 */ +/***/ (function(module, exports, __webpack_require__) { + +var baseMatches = __webpack_require__(267), + baseMatchesProperty = __webpack_require__(310), + identity = __webpack_require__(17), + isArray = __webpack_require__(2), + property = __webpack_require__(320); + +/** + * The base implementation of `_.iteratee`. + * + * @private + * @param {*} [value=_.identity] The value to convert to an iteratee. + * @returns {Function} Returns the iteratee. + */ +function baseIteratee(value) { + // Don't store the `typeof` result in a variable to avoid a JIT bug in Safari 9. + // See https://bugs.webkit.org/show_bug.cgi?id=156034 for more details. + if (typeof value == 'function') { + return value; + } + if (value == null) { + return identity; + } + if (typeof value == 'object') { + return isArray(value) + ? baseMatchesProperty(value[0], value[1]) + : baseMatches(value); + } + return property(value); +} + +module.exports = baseIteratee; + + +/***/ }), +/* 16 */ +/***/ (function(module, exports, __webpack_require__) { + +var baseGetTag = __webpack_require__(11), + isObjectLike = __webpack_require__(12); + +/** `Object#toString` result references. */ +var symbolTag = '[object Symbol]'; + +/** + * Checks if `value` is classified as a `Symbol` primitive or object. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a symbol, else `false`. + * @example + * + * _.isSymbol(Symbol.iterator); + * // => true + * + * _.isSymbol('abc'); + * // => false + */ +function isSymbol(value) { + return typeof value == 'symbol' || + (isObjectLike(value) && baseGetTag(value) == symbolTag); +} + +module.exports = isSymbol; + + +/***/ }), +/* 17 */ +/***/ (function(module, exports) { + +/** + * This method returns the first argument it receives. + * + * @static + * @since 0.1.0 + * @memberOf _ + * @category Util + * @param {*} value Any value. + * @returns {*} Returns `value`. + * @example + * + * var object = { 'a': 1 }; + * + * console.log(_.identity(object) === object); + * // => true + */ +function identity(value) { + return value; +} + +module.exports = identity; + + +/***/ }), +/* 18 */ +/***/ (function(module, exports) { + +var g; + +// This works in non-strict mode +g = (function() { + return this; +})(); + +try { + // This works if eval is allowed (see CSP) + g = g || Function("return this")() || (1,eval)("this"); +} catch(e) { + // This works if the window reference is available + if(typeof window === "object") + g = window; +} + +// g can still be undefined, but nothing to do about it... +// We return undefined, instead of nothing here, so it's +// easier to handle this case. if(!global) { ...} + +module.exports = g; + + +/***/ }), +/* 19 */ +/***/ (function(module, exports, __webpack_require__) { + +/* WEBPACK VAR INJECTION */(function(global, module) {/** + * @license + * Lodash + * Copyright JS Foundation and other contributors + * Released under MIT license + * Based on Underscore.js 1.8.3 + * Copyright Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors + */ +;(function() { + + /** Used as a safe reference for `undefined` in pre-ES5 environments. */ + var undefined; + + /** Used as the semantic version number. */ + var VERSION = '4.17.4'; + + /** Used as the size to enable large array optimizations. */ + var LARGE_ARRAY_SIZE = 200; + + /** Error message constants. */ + var CORE_ERROR_TEXT = 'Unsupported core-js use. Try https://npms.io/search?q=ponyfill.', + FUNC_ERROR_TEXT = 'Expected a function'; + + /** Used to stand-in for `undefined` hash values. */ + var HASH_UNDEFINED = '__lodash_hash_undefined__'; + + /** Used as the maximum memoize cache size. */ + var MAX_MEMOIZE_SIZE = 500; + + /** Used as the internal argument placeholder. */ + var PLACEHOLDER = '__lodash_placeholder__'; + + /** Used to compose bitmasks for cloning. */ + var CLONE_DEEP_FLAG = 1, + CLONE_FLAT_FLAG = 2, + CLONE_SYMBOLS_FLAG = 4; + + /** Used to compose bitmasks for value comparisons. */ + var COMPARE_PARTIAL_FLAG = 1, + COMPARE_UNORDERED_FLAG = 2; + + /** Used to compose bitmasks for function metadata. */ + var WRAP_BIND_FLAG = 1, + WRAP_BIND_KEY_FLAG = 2, + WRAP_CURRY_BOUND_FLAG = 4, + WRAP_CURRY_FLAG = 8, + WRAP_CURRY_RIGHT_FLAG = 16, + WRAP_PARTIAL_FLAG = 32, + WRAP_PARTIAL_RIGHT_FLAG = 64, + WRAP_ARY_FLAG = 128, + WRAP_REARG_FLAG = 256, + WRAP_FLIP_FLAG = 512; + + /** Used as default options for `_.truncate`. */ + var DEFAULT_TRUNC_LENGTH = 30, + DEFAULT_TRUNC_OMISSION = '...'; + + /** Used to detect hot functions by number of calls within a span of milliseconds. */ + var HOT_COUNT = 800, + HOT_SPAN = 16; + + /** Used to indicate the type of lazy iteratees. */ + var LAZY_FILTER_FLAG = 1, + LAZY_MAP_FLAG = 2, + LAZY_WHILE_FLAG = 3; + + /** Used as references for various `Number` constants. */ + var INFINITY = 1 / 0, + MAX_SAFE_INTEGER = 9007199254740991, + MAX_INTEGER = 1.7976931348623157e+308, + NAN = 0 / 0; + + /** Used as references for the maximum length and index of an array. */ + var MAX_ARRAY_LENGTH = 4294967295, + MAX_ARRAY_INDEX = MAX_ARRAY_LENGTH - 1, + HALF_MAX_ARRAY_LENGTH = MAX_ARRAY_LENGTH >>> 1; + + /** Used to associate wrap methods with their bit flags. */ + var wrapFlags = [ + ['ary', WRAP_ARY_FLAG], + ['bind', WRAP_BIND_FLAG], + ['bindKey', WRAP_BIND_KEY_FLAG], + ['curry', WRAP_CURRY_FLAG], + ['curryRight', WRAP_CURRY_RIGHT_FLAG], + ['flip', WRAP_FLIP_FLAG], + ['partial', WRAP_PARTIAL_FLAG], + ['partialRight', WRAP_PARTIAL_RIGHT_FLAG], + ['rearg', WRAP_REARG_FLAG] + ]; + + /** `Object#toString` result references. */ + var argsTag = '[object Arguments]', + arrayTag = '[object Array]', + asyncTag = '[object AsyncFunction]', + boolTag = '[object Boolean]', + dateTag = '[object Date]', + domExcTag = '[object DOMException]', + errorTag = '[object Error]', + funcTag = '[object Function]', + genTag = '[object GeneratorFunction]', + mapTag = '[object Map]', + numberTag = '[object Number]', + nullTag = '[object Null]', + objectTag = '[object Object]', + promiseTag = '[object Promise]', + proxyTag = '[object Proxy]', + regexpTag = '[object RegExp]', + setTag = '[object Set]', + stringTag = '[object String]', + symbolTag = '[object Symbol]', + undefinedTag = '[object Undefined]', + weakMapTag = '[object WeakMap]', + weakSetTag = '[object WeakSet]'; + + var arrayBufferTag = '[object ArrayBuffer]', + dataViewTag = '[object DataView]', + float32Tag = '[object Float32Array]', + float64Tag = '[object Float64Array]', + int8Tag = '[object Int8Array]', + int16Tag = '[object Int16Array]', + int32Tag = '[object Int32Array]', + uint8Tag = '[object Uint8Array]', + uint8ClampedTag = '[object Uint8ClampedArray]', + uint16Tag = '[object Uint16Array]', + uint32Tag = '[object Uint32Array]'; + + /** Used to match empty string literals in compiled template source. */ + var reEmptyStringLeading = /\b__p \+= '';/g, + reEmptyStringMiddle = /\b(__p \+=) '' \+/g, + reEmptyStringTrailing = /(__e\(.*?\)|\b__t\)) \+\n'';/g; + + /** Used to match HTML entities and HTML characters. */ + var reEscapedHtml = /&(?:amp|lt|gt|quot|#39);/g, + reUnescapedHtml = /[&<>"']/g, + reHasEscapedHtml = RegExp(reEscapedHtml.source), + reHasUnescapedHtml = RegExp(reUnescapedHtml.source); + + /** Used to match template delimiters. */ + var reEscape = /<%-([\s\S]+?)%>/g, + reEvaluate = /<%([\s\S]+?)%>/g, + reInterpolate = /<%=([\s\S]+?)%>/g; + + /** Used to match property names within property paths. */ + var reIsDeepProp = /\.|\[(?:[^[\]]*|(["'])(?:(?!\1)[^\\]|\\.)*?\1)\]/, + reIsPlainProp = /^\w*$/, + reLeadingDot = /^\./, + rePropName = /[^.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\\]|\\.)*?)\2)\]|(?=(?:\.|\[\])(?:\.|\[\]|$))/g; + + /** + * Used to match `RegExp` + * [syntax characters](http://ecma-international.org/ecma-262/7.0/#sec-patterns). + */ + var reRegExpChar = /[\\^$.*+?()[\]{}|]/g, + reHasRegExpChar = RegExp(reRegExpChar.source); + + /** Used to match leading and trailing whitespace. */ + var reTrim = /^\s+|\s+$/g, + reTrimStart = /^\s+/, + reTrimEnd = /\s+$/; + + /** Used to match wrap detail comments. */ + var reWrapComment = /\{(?:\n\/\* \[wrapped with .+\] \*\/)?\n?/, + reWrapDetails = /\{\n\/\* \[wrapped with (.+)\] \*/, + reSplitDetails = /,? & /; + + /** Used to match words composed of alphanumeric characters. */ + var reAsciiWord = /[^\x00-\x2f\x3a-\x40\x5b-\x60\x7b-\x7f]+/g; + + /** Used to match backslashes in property paths. */ + var reEscapeChar = /\\(\\)?/g; + + /** + * Used to match + * [ES template delimiters](http://ecma-international.org/ecma-262/7.0/#sec-template-literal-lexical-components). + */ + var reEsTemplate = /\$\{([^\\}]*(?:\\.[^\\}]*)*)\}/g; + + /** Used to match `RegExp` flags from their coerced string values. */ + var reFlags = /\w*$/; + + /** Used to detect bad signed hexadecimal string values. */ + var reIsBadHex = /^[-+]0x[0-9a-f]+$/i; + + /** Used to detect binary string values. */ + var reIsBinary = /^0b[01]+$/i; + + /** Used to detect host constructors (Safari). */ + var reIsHostCtor = /^\[object .+?Constructor\]$/; + + /** Used to detect octal string values. */ + var reIsOctal = /^0o[0-7]+$/i; + + /** Used to detect unsigned integer values. */ + var reIsUint = /^(?:0|[1-9]\d*)$/; + + /** Used to match Latin Unicode letters (excluding mathematical operators). */ + var reLatin = /[\xc0-\xd6\xd8-\xf6\xf8-\xff\u0100-\u017f]/g; + + /** Used to ensure capturing order of template delimiters. */ + var reNoMatch = /($^)/; + + /** Used to match unescaped characters in compiled string literals. */ + var reUnescapedString = /['\n\r\u2028\u2029\\]/g; + + /** Used to compose unicode character classes. */ + var rsAstralRange = '\\ud800-\\udfff', + rsComboMarksRange = '\\u0300-\\u036f', + reComboHalfMarksRange = '\\ufe20-\\ufe2f', + rsComboSymbolsRange = '\\u20d0-\\u20ff', + rsComboRange = rsComboMarksRange + reComboHalfMarksRange + rsComboSymbolsRange, + rsDingbatRange = '\\u2700-\\u27bf', + rsLowerRange = 'a-z\\xdf-\\xf6\\xf8-\\xff', + rsMathOpRange = '\\xac\\xb1\\xd7\\xf7', + rsNonCharRange = '\\x00-\\x2f\\x3a-\\x40\\x5b-\\x60\\x7b-\\xbf', + rsPunctuationRange = '\\u2000-\\u206f', + rsSpaceRange = ' \\t\\x0b\\f\\xa0\\ufeff\\n\\r\\u2028\\u2029\\u1680\\u180e\\u2000\\u2001\\u2002\\u2003\\u2004\\u2005\\u2006\\u2007\\u2008\\u2009\\u200a\\u202f\\u205f\\u3000', + rsUpperRange = 'A-Z\\xc0-\\xd6\\xd8-\\xde', + rsVarRange = '\\ufe0e\\ufe0f', + rsBreakRange = rsMathOpRange + rsNonCharRange + rsPunctuationRange + rsSpaceRange; + + /** Used to compose unicode capture groups. */ + var rsApos = "['\u2019]", + rsAstral = '[' + rsAstralRange + ']', + rsBreak = '[' + rsBreakRange + ']', + rsCombo = '[' + rsComboRange + ']', + rsDigits = '\\d+', + rsDingbat = '[' + rsDingbatRange + ']', + rsLower = '[' + rsLowerRange + ']', + rsMisc = '[^' + rsAstralRange + rsBreakRange + rsDigits + rsDingbatRange + rsLowerRange + rsUpperRange + ']', + rsFitz = '\\ud83c[\\udffb-\\udfff]', + rsModifier = '(?:' + rsCombo + '|' + rsFitz + ')', + rsNonAstral = '[^' + rsAstralRange + ']', + rsRegional = '(?:\\ud83c[\\udde6-\\uddff]){2}', + rsSurrPair = '[\\ud800-\\udbff][\\udc00-\\udfff]', + rsUpper = '[' + rsUpperRange + ']', + rsZWJ = '\\u200d'; + + /** Used to compose unicode regexes. */ + var rsMiscLower = '(?:' + rsLower + '|' + rsMisc + ')', + rsMiscUpper = '(?:' + rsUpper + '|' + rsMisc + ')', + rsOptContrLower = '(?:' + rsApos + '(?:d|ll|m|re|s|t|ve))?', + rsOptContrUpper = '(?:' + rsApos + '(?:D|LL|M|RE|S|T|VE))?', + reOptMod = rsModifier + '?', + rsOptVar = '[' + rsVarRange + ']?', + rsOptJoin = '(?:' + rsZWJ + '(?:' + [rsNonAstral, rsRegional, rsSurrPair].join('|') + ')' + rsOptVar + reOptMod + ')*', + rsOrdLower = '\\d*(?:(?:1st|2nd|3rd|(?![123])\\dth)\\b)', + rsOrdUpper = '\\d*(?:(?:1ST|2ND|3RD|(?![123])\\dTH)\\b)', + rsSeq = rsOptVar + reOptMod + rsOptJoin, + rsEmoji = '(?:' + [rsDingbat, rsRegional, rsSurrPair].join('|') + ')' + rsSeq, + rsSymbol = '(?:' + [rsNonAstral + rsCombo + '?', rsCombo, rsRegional, rsSurrPair, rsAstral].join('|') + ')'; + + /** Used to match apostrophes. */ + var reApos = RegExp(rsApos, 'g'); + + /** + * Used to match [combining diacritical marks](https://en.wikipedia.org/wiki/Combining_Diacritical_Marks) and + * [combining diacritical marks for symbols](https://en.wikipedia.org/wiki/Combining_Diacritical_Marks_for_Symbols). + */ + var reComboMark = RegExp(rsCombo, 'g'); + + /** Used to match [string symbols](https://mathiasbynens.be/notes/javascript-unicode). */ + var reUnicode = RegExp(rsFitz + '(?=' + rsFitz + ')|' + rsSymbol + rsSeq, 'g'); + + /** Used to match complex or compound words. */ + var reUnicodeWord = RegExp([ + rsUpper + '?' + rsLower + '+' + rsOptContrLower + '(?=' + [rsBreak, rsUpper, '$'].join('|') + ')', + rsMiscUpper + '+' + rsOptContrUpper + '(?=' + [rsBreak, rsUpper + rsMiscLower, '$'].join('|') + ')', + rsUpper + '?' + rsMiscLower + '+' + rsOptContrLower, + rsUpper + '+' + rsOptContrUpper, + rsOrdUpper, + rsOrdLower, + rsDigits, + rsEmoji + ].join('|'), 'g'); + + /** Used to detect strings with [zero-width joiners or code points from the astral planes](http://eev.ee/blog/2015/09/12/dark-corners-of-unicode/). */ + var reHasUnicode = RegExp('[' + rsZWJ + rsAstralRange + rsComboRange + rsVarRange + ']'); + + /** Used to detect strings that need a more robust regexp to match words. */ + var reHasUnicodeWord = /[a-z][A-Z]|[A-Z]{2,}[a-z]|[0-9][a-zA-Z]|[a-zA-Z][0-9]|[^a-zA-Z0-9 ]/; + + /** Used to assign default `context` object properties. */ + var contextProps = [ + 'Array', 'Buffer', 'DataView', 'Date', 'Error', 'Float32Array', 'Float64Array', + 'Function', 'Int8Array', 'Int16Array', 'Int32Array', 'Map', 'Math', 'Object', + 'Promise', 'RegExp', 'Set', 'String', 'Symbol', 'TypeError', 'Uint8Array', + 'Uint8ClampedArray', 'Uint16Array', 'Uint32Array', 'WeakMap', + '_', 'clearTimeout', 'isFinite', 'parseInt', 'setTimeout' + ]; + + /** Used to make template sourceURLs easier to identify. */ + var templateCounter = -1; + + /** Used to identify `toStringTag` values of typed arrays. */ + var typedArrayTags = {}; + typedArrayTags[float32Tag] = typedArrayTags[float64Tag] = + typedArrayTags[int8Tag] = typedArrayTags[int16Tag] = + typedArrayTags[int32Tag] = typedArrayTags[uint8Tag] = + typedArrayTags[uint8ClampedTag] = typedArrayTags[uint16Tag] = + typedArrayTags[uint32Tag] = true; + typedArrayTags[argsTag] = typedArrayTags[arrayTag] = + typedArrayTags[arrayBufferTag] = typedArrayTags[boolTag] = + typedArrayTags[dataViewTag] = typedArrayTags[dateTag] = + typedArrayTags[errorTag] = typedArrayTags[funcTag] = + typedArrayTags[mapTag] = typedArrayTags[numberTag] = + typedArrayTags[objectTag] = typedArrayTags[regexpTag] = + typedArrayTags[setTag] = typedArrayTags[stringTag] = + typedArrayTags[weakMapTag] = false; + + /** Used to identify `toStringTag` values supported by `_.clone`. */ + var cloneableTags = {}; + cloneableTags[argsTag] = cloneableTags[arrayTag] = + cloneableTags[arrayBufferTag] = cloneableTags[dataViewTag] = + cloneableTags[boolTag] = cloneableTags[dateTag] = + cloneableTags[float32Tag] = cloneableTags[float64Tag] = + cloneableTags[int8Tag] = cloneableTags[int16Tag] = + cloneableTags[int32Tag] = cloneableTags[mapTag] = + cloneableTags[numberTag] = cloneableTags[objectTag] = + cloneableTags[regexpTag] = cloneableTags[setTag] = + cloneableTags[stringTag] = cloneableTags[symbolTag] = + cloneableTags[uint8Tag] = cloneableTags[uint8ClampedTag] = + cloneableTags[uint16Tag] = cloneableTags[uint32Tag] = true; + cloneableTags[errorTag] = cloneableTags[funcTag] = + cloneableTags[weakMapTag] = false; + + /** Used to map Latin Unicode letters to basic Latin letters. */ + var deburredLetters = { + // Latin-1 Supplement block. + '\xc0': 'A', '\xc1': 'A', '\xc2': 'A', '\xc3': 'A', '\xc4': 'A', '\xc5': 'A', + '\xe0': 'a', '\xe1': 'a', '\xe2': 'a', '\xe3': 'a', '\xe4': 'a', '\xe5': 'a', + '\xc7': 'C', '\xe7': 'c', + '\xd0': 'D', '\xf0': 'd', + '\xc8': 'E', '\xc9': 'E', '\xca': 'E', '\xcb': 'E', + '\xe8': 'e', '\xe9': 'e', '\xea': 'e', '\xeb': 'e', + '\xcc': 'I', '\xcd': 'I', '\xce': 'I', '\xcf': 'I', + '\xec': 'i', '\xed': 'i', '\xee': 'i', '\xef': 'i', + '\xd1': 'N', '\xf1': 'n', + '\xd2': 'O', '\xd3': 'O', '\xd4': 'O', '\xd5': 'O', '\xd6': 'O', '\xd8': 'O', + '\xf2': 'o', '\xf3': 'o', '\xf4': 'o', '\xf5': 'o', '\xf6': 'o', '\xf8': 'o', + '\xd9': 'U', '\xda': 'U', '\xdb': 'U', '\xdc': 'U', + '\xf9': 'u', '\xfa': 'u', '\xfb': 'u', '\xfc': 'u', + '\xdd': 'Y', '\xfd': 'y', '\xff': 'y', + '\xc6': 'Ae', '\xe6': 'ae', + '\xde': 'Th', '\xfe': 'th', + '\xdf': 'ss', + // Latin Extended-A block. + '\u0100': 'A', '\u0102': 'A', '\u0104': 'A', + '\u0101': 'a', '\u0103': 'a', '\u0105': 'a', + '\u0106': 'C', '\u0108': 'C', '\u010a': 'C', '\u010c': 'C', + '\u0107': 'c', '\u0109': 'c', '\u010b': 'c', '\u010d': 'c', + '\u010e': 'D', '\u0110': 'D', '\u010f': 'd', '\u0111': 'd', + '\u0112': 'E', '\u0114': 'E', '\u0116': 'E', '\u0118': 'E', '\u011a': 'E', + '\u0113': 'e', '\u0115': 'e', '\u0117': 'e', '\u0119': 'e', '\u011b': 'e', + '\u011c': 'G', '\u011e': 'G', '\u0120': 'G', '\u0122': 'G', + '\u011d': 'g', '\u011f': 'g', '\u0121': 'g', '\u0123': 'g', + '\u0124': 'H', '\u0126': 'H', '\u0125': 'h', '\u0127': 'h', + '\u0128': 'I', '\u012a': 'I', '\u012c': 'I', '\u012e': 'I', '\u0130': 'I', + '\u0129': 'i', '\u012b': 'i', '\u012d': 'i', '\u012f': 'i', '\u0131': 'i', + '\u0134': 'J', '\u0135': 'j', + '\u0136': 'K', '\u0137': 'k', '\u0138': 'k', + '\u0139': 'L', '\u013b': 'L', '\u013d': 'L', '\u013f': 'L', '\u0141': 'L', + '\u013a': 'l', '\u013c': 'l', '\u013e': 'l', '\u0140': 'l', '\u0142': 'l', + '\u0143': 'N', '\u0145': 'N', '\u0147': 'N', '\u014a': 'N', + '\u0144': 'n', '\u0146': 'n', '\u0148': 'n', '\u014b': 'n', + '\u014c': 'O', '\u014e': 'O', '\u0150': 'O', + '\u014d': 'o', '\u014f': 'o', '\u0151': 'o', + '\u0154': 'R', '\u0156': 'R', '\u0158': 'R', + '\u0155': 'r', '\u0157': 'r', '\u0159': 'r', + '\u015a': 'S', '\u015c': 'S', '\u015e': 'S', '\u0160': 'S', + '\u015b': 's', '\u015d': 's', '\u015f': 's', '\u0161': 's', + '\u0162': 'T', '\u0164': 'T', '\u0166': 'T', + '\u0163': 't', '\u0165': 't', '\u0167': 't', + '\u0168': 'U', '\u016a': 'U', '\u016c': 'U', '\u016e': 'U', '\u0170': 'U', '\u0172': 'U', + '\u0169': 'u', '\u016b': 'u', '\u016d': 'u', '\u016f': 'u', '\u0171': 'u', '\u0173': 'u', + '\u0174': 'W', '\u0175': 'w', + '\u0176': 'Y', '\u0177': 'y', '\u0178': 'Y', + '\u0179': 'Z', '\u017b': 'Z', '\u017d': 'Z', + '\u017a': 'z', '\u017c': 'z', '\u017e': 'z', + '\u0132': 'IJ', '\u0133': 'ij', + '\u0152': 'Oe', '\u0153': 'oe', + '\u0149': "'n", '\u017f': 's' + }; + + /** Used to map characters to HTML entities. */ + var htmlEscapes = { + '&': '&', + '<': '<', + '>': '>', + '"': '"', + "'": ''' + }; + + /** Used to map HTML entities to characters. */ + var htmlUnescapes = { + '&': '&', + '<': '<', + '>': '>', + '"': '"', + ''': "'" + }; + + /** Used to escape characters for inclusion in compiled string literals. */ + var stringEscapes = { + '\\': '\\', + "'": "'", + '\n': 'n', + '\r': 'r', + '\u2028': 'u2028', + '\u2029': 'u2029' + }; + + /** Built-in method references without a dependency on `root`. */ + var freeParseFloat = parseFloat, + freeParseInt = parseInt; + + /** Detect free variable `global` from Node.js. */ + var freeGlobal = typeof global == 'object' && global && global.Object === Object && global; + + /** Detect free variable `self`. */ + var freeSelf = typeof self == 'object' && self && self.Object === Object && self; + + /** Used as a reference to the global object. */ + var root = freeGlobal || freeSelf || Function('return this')(); + + /** Detect free variable `exports`. */ + var freeExports = typeof exports == 'object' && exports && !exports.nodeType && exports; + + /** Detect free variable `module`. */ + var freeModule = freeExports && typeof module == 'object' && module && !module.nodeType && module; + + /** Detect the popular CommonJS extension `module.exports`. */ + var moduleExports = freeModule && freeModule.exports === freeExports; + + /** Detect free variable `process` from Node.js. */ + var freeProcess = moduleExports && freeGlobal.process; + + /** Used to access faster Node.js helpers. */ + var nodeUtil = (function() { + try { + return freeProcess && freeProcess.binding && freeProcess.binding('util'); + } catch (e) {} + }()); + + /* Node.js helper references. */ + var nodeIsArrayBuffer = nodeUtil && nodeUtil.isArrayBuffer, + nodeIsDate = nodeUtil && nodeUtil.isDate, + nodeIsMap = nodeUtil && nodeUtil.isMap, + nodeIsRegExp = nodeUtil && nodeUtil.isRegExp, + nodeIsSet = nodeUtil && nodeUtil.isSet, + nodeIsTypedArray = nodeUtil && nodeUtil.isTypedArray; + + /*--------------------------------------------------------------------------*/ + + /** + * Adds the key-value `pair` to `map`. + * + * @private + * @param {Object} map The map to modify. + * @param {Array} pair The key-value pair to add. + * @returns {Object} Returns `map`. + */ + function addMapEntry(map, pair) { + // Don't return `map.set` because it's not chainable in IE 11. + map.set(pair[0], pair[1]); + return map; + } + + /** + * Adds `value` to `set`. + * + * @private + * @param {Object} set The set to modify. + * @param {*} value The value to add. + * @returns {Object} Returns `set`. + */ + function addSetEntry(set, value) { + // Don't return `set.add` because it's not chainable in IE 11. + set.add(value); + return set; + } + + /** + * A faster alternative to `Function#apply`, this function invokes `func` + * with the `this` binding of `thisArg` and the arguments of `args`. + * + * @private + * @param {Function} func The function to invoke. + * @param {*} thisArg The `this` binding of `func`. + * @param {Array} args The arguments to invoke `func` with. + * @returns {*} Returns the result of `func`. + */ + function apply(func, thisArg, args) { + switch (args.length) { + case 0: return func.call(thisArg); + case 1: return func.call(thisArg, args[0]); + case 2: return func.call(thisArg, args[0], args[1]); + case 3: return func.call(thisArg, args[0], args[1], args[2]); + } + return func.apply(thisArg, args); + } + + /** + * A specialized version of `baseAggregator` for arrays. + * + * @private + * @param {Array} [array] The array to iterate over. + * @param {Function} setter The function to set `accumulator` values. + * @param {Function} iteratee The iteratee to transform keys. + * @param {Object} accumulator The initial aggregated object. + * @returns {Function} Returns `accumulator`. + */ + function arrayAggregator(array, setter, iteratee, accumulator) { + var index = -1, + length = array == null ? 0 : array.length; + + while (++index < length) { + var value = array[index]; + setter(accumulator, value, iteratee(value), array); + } + return accumulator; + } + + /** + * A specialized version of `_.forEach` for arrays without support for + * iteratee shorthands. + * + * @private + * @param {Array} [array] The array to iterate over. + * @param {Function} iteratee The function invoked per iteration. + * @returns {Array} Returns `array`. + */ + function arrayEach(array, iteratee) { + var index = -1, + length = array == null ? 0 : array.length; + + while (++index < length) { + if (iteratee(array[index], index, array) === false) { + break; + } + } + return array; + } + + /** + * A specialized version of `_.forEachRight` for arrays without support for + * iteratee shorthands. + * + * @private + * @param {Array} [array] The array to iterate over. + * @param {Function} iteratee The function invoked per iteration. + * @returns {Array} Returns `array`. + */ + function arrayEachRight(array, iteratee) { + var length = array == null ? 0 : array.length; + + while (length--) { + if (iteratee(array[length], length, array) === false) { + break; + } + } + return array; + } + + /** + * A specialized version of `_.every` for arrays without support for + * iteratee shorthands. + * + * @private + * @param {Array} [array] The array to iterate over. + * @param {Function} predicate The function invoked per iteration. + * @returns {boolean} Returns `true` if all elements pass the predicate check, + * else `false`. + */ + function arrayEvery(array, predicate) { + var index = -1, + length = array == null ? 0 : array.length; + + while (++index < length) { + if (!predicate(array[index], index, array)) { + return false; + } + } + return true; + } + + /** + * A specialized version of `_.filter` for arrays without support for + * iteratee shorthands. + * + * @private + * @param {Array} [array] The array to iterate over. + * @param {Function} predicate The function invoked per iteration. + * @returns {Array} Returns the new filtered array. + */ + function arrayFilter(array, predicate) { + var index = -1, + length = array == null ? 0 : array.length, + resIndex = 0, + result = []; + + while (++index < length) { + var value = array[index]; + if (predicate(value, index, array)) { + result[resIndex++] = value; + } + } + return result; + } + + /** + * A specialized version of `_.includes` for arrays without support for + * specifying an index to search from. + * + * @private + * @param {Array} [array] The array to inspect. + * @param {*} target The value to search for. + * @returns {boolean} Returns `true` if `target` is found, else `false`. + */ + function arrayIncludes(array, value) { + var length = array == null ? 0 : array.length; + return !!length && baseIndexOf(array, value, 0) > -1; + } + + /** + * This function is like `arrayIncludes` except that it accepts a comparator. + * + * @private + * @param {Array} [array] The array to inspect. + * @param {*} target The value to search for. + * @param {Function} comparator The comparator invoked per element. + * @returns {boolean} Returns `true` if `target` is found, else `false`. + */ + function arrayIncludesWith(array, value, comparator) { + var index = -1, + length = array == null ? 0 : array.length; + + while (++index < length) { + if (comparator(value, array[index])) { + return true; + } + } + return false; + } + + /** + * A specialized version of `_.map` for arrays without support for iteratee + * shorthands. + * + * @private + * @param {Array} [array] The array to iterate over. + * @param {Function} iteratee The function invoked per iteration. + * @returns {Array} Returns the new mapped array. + */ + function arrayMap(array, iteratee) { + var index = -1, + length = array == null ? 0 : array.length, + result = Array(length); + + while (++index < length) { + result[index] = iteratee(array[index], index, array); + } + return result; + } + + /** + * Appends the elements of `values` to `array`. + * + * @private + * @param {Array} array The array to modify. + * @param {Array} values The values to append. + * @returns {Array} Returns `array`. + */ + function arrayPush(array, values) { + var index = -1, + length = values.length, + offset = array.length; + + while (++index < length) { + array[offset + index] = values[index]; + } + return array; + } + + /** + * A specialized version of `_.reduce` for arrays without support for + * iteratee shorthands. + * + * @private + * @param {Array} [array] The array to iterate over. + * @param {Function} iteratee The function invoked per iteration. + * @param {*} [accumulator] The initial value. + * @param {boolean} [initAccum] Specify using the first element of `array` as + * the initial value. + * @returns {*} Returns the accumulated value. + */ + function arrayReduce(array, iteratee, accumulator, initAccum) { + var index = -1, + length = array == null ? 0 : array.length; + + if (initAccum && length) { + accumulator = array[++index]; + } + while (++index < length) { + accumulator = iteratee(accumulator, array[index], index, array); + } + return accumulator; + } + + /** + * A specialized version of `_.reduceRight` for arrays without support for + * iteratee shorthands. + * + * @private + * @param {Array} [array] The array to iterate over. + * @param {Function} iteratee The function invoked per iteration. + * @param {*} [accumulator] The initial value. + * @param {boolean} [initAccum] Specify using the last element of `array` as + * the initial value. + * @returns {*} Returns the accumulated value. + */ + function arrayReduceRight(array, iteratee, accumulator, initAccum) { + var length = array == null ? 0 : array.length; + if (initAccum && length) { + accumulator = array[--length]; + } + while (length--) { + accumulator = iteratee(accumulator, array[length], length, array); + } + return accumulator; + } + + /** + * A specialized version of `_.some` for arrays without support for iteratee + * shorthands. + * + * @private + * @param {Array} [array] The array to iterate over. + * @param {Function} predicate The function invoked per iteration. + * @returns {boolean} Returns `true` if any element passes the predicate check, + * else `false`. + */ + function arraySome(array, predicate) { + var index = -1, + length = array == null ? 0 : array.length; + + while (++index < length) { + if (predicate(array[index], index, array)) { + return true; + } + } + return false; + } + + /** + * Gets the size of an ASCII `string`. + * + * @private + * @param {string} string The string inspect. + * @returns {number} Returns the string size. + */ + var asciiSize = baseProperty('length'); + + /** + * Converts an ASCII `string` to an array. + * + * @private + * @param {string} string The string to convert. + * @returns {Array} Returns the converted array. + */ + function asciiToArray(string) { + return string.split(''); + } + + /** + * Splits an ASCII `string` into an array of its words. + * + * @private + * @param {string} The string to inspect. + * @returns {Array} Returns the words of `string`. + */ + function asciiWords(string) { + return string.match(reAsciiWord) || []; + } + + /** + * The base implementation of methods like `_.findKey` and `_.findLastKey`, + * without support for iteratee shorthands, which iterates over `collection` + * using `eachFunc`. + * + * @private + * @param {Array|Object} collection The collection to inspect. + * @param {Function} predicate The function invoked per iteration. + * @param {Function} eachFunc The function to iterate over `collection`. + * @returns {*} Returns the found element or its key, else `undefined`. + */ + function baseFindKey(collection, predicate, eachFunc) { + var result; + eachFunc(collection, function(value, key, collection) { + if (predicate(value, key, collection)) { + result = key; + return false; + } + }); + return result; + } + + /** + * The base implementation of `_.findIndex` and `_.findLastIndex` without + * support for iteratee shorthands. + * + * @private + * @param {Array} array The array to inspect. + * @param {Function} predicate The function invoked per iteration. + * @param {number} fromIndex The index to search from. + * @param {boolean} [fromRight] Specify iterating from right to left. + * @returns {number} Returns the index of the matched value, else `-1`. + */ + function baseFindIndex(array, predicate, fromIndex, fromRight) { + var length = array.length, + index = fromIndex + (fromRight ? 1 : -1); + + while ((fromRight ? index-- : ++index < length)) { + if (predicate(array[index], index, array)) { + return index; + } + } + return -1; + } + + /** + * The base implementation of `_.indexOf` without `fromIndex` bounds checks. + * + * @private + * @param {Array} array The array to inspect. + * @param {*} value The value to search for. + * @param {number} fromIndex The index to search from. + * @returns {number} Returns the index of the matched value, else `-1`. + */ + function baseIndexOf(array, value, fromIndex) { + return value === value + ? strictIndexOf(array, value, fromIndex) + : baseFindIndex(array, baseIsNaN, fromIndex); + } + + /** + * This function is like `baseIndexOf` except that it accepts a comparator. + * + * @private + * @param {Array} array The array to inspect. + * @param {*} value The value to search for. + * @param {number} fromIndex The index to search from. + * @param {Function} comparator The comparator invoked per element. + * @returns {number} Returns the index of the matched value, else `-1`. + */ + function baseIndexOfWith(array, value, fromIndex, comparator) { + var index = fromIndex - 1, + length = array.length; + + while (++index < length) { + if (comparator(array[index], value)) { + return index; + } + } + return -1; + } + + /** + * The base implementation of `_.isNaN` without support for number objects. + * + * @private + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is `NaN`, else `false`. + */ + function baseIsNaN(value) { + return value !== value; + } + + /** + * The base implementation of `_.mean` and `_.meanBy` without support for + * iteratee shorthands. + * + * @private + * @param {Array} array The array to iterate over. + * @param {Function} iteratee The function invoked per iteration. + * @returns {number} Returns the mean. + */ + function baseMean(array, iteratee) { + var length = array == null ? 0 : array.length; + return length ? (baseSum(array, iteratee) / length) : NAN; + } + + /** + * The base implementation of `_.property` without support for deep paths. + * + * @private + * @param {string} key The key of the property to get. + * @returns {Function} Returns the new accessor function. + */ + function baseProperty(key) { + return function(object) { + return object == null ? undefined : object[key]; + }; + } + + /** + * The base implementation of `_.propertyOf` without support for deep paths. + * + * @private + * @param {Object} object The object to query. + * @returns {Function} Returns the new accessor function. + */ + function basePropertyOf(object) { + return function(key) { + return object == null ? undefined : object[key]; + }; + } + + /** + * The base implementation of `_.reduce` and `_.reduceRight`, without support + * for iteratee shorthands, which iterates over `collection` using `eachFunc`. + * + * @private + * @param {Array|Object} collection The collection to iterate over. + * @param {Function} iteratee The function invoked per iteration. + * @param {*} accumulator The initial value. + * @param {boolean} initAccum Specify using the first or last element of + * `collection` as the initial value. + * @param {Function} eachFunc The function to iterate over `collection`. + * @returns {*} Returns the accumulated value. + */ + function baseReduce(collection, iteratee, accumulator, initAccum, eachFunc) { + eachFunc(collection, function(value, index, collection) { + accumulator = initAccum + ? (initAccum = false, value) + : iteratee(accumulator, value, index, collection); + }); + return accumulator; + } + + /** + * The base implementation of `_.sortBy` which uses `comparer` to define the + * sort order of `array` and replaces criteria objects with their corresponding + * values. + * + * @private + * @param {Array} array The array to sort. + * @param {Function} comparer The function to define sort order. + * @returns {Array} Returns `array`. + */ + function baseSortBy(array, comparer) { + var length = array.length; + + array.sort(comparer); + while (length--) { + array[length] = array[length].value; + } + return array; + } + + /** + * The base implementation of `_.sum` and `_.sumBy` without support for + * iteratee shorthands. + * + * @private + * @param {Array} array The array to iterate over. + * @param {Function} iteratee The function invoked per iteration. + * @returns {number} Returns the sum. + */ + function baseSum(array, iteratee) { + var result, + index = -1, + length = array.length; + + while (++index < length) { + var current = iteratee(array[index]); + if (current !== undefined) { + result = result === undefined ? current : (result + current); + } + } + return result; + } + + /** + * The base implementation of `_.times` without support for iteratee shorthands + * or max array length checks. + * + * @private + * @param {number} n The number of times to invoke `iteratee`. + * @param {Function} iteratee The function invoked per iteration. + * @returns {Array} Returns the array of results. + */ + function baseTimes(n, iteratee) { + var index = -1, + result = Array(n); + + while (++index < n) { + result[index] = iteratee(index); + } + return result; + } + + /** + * The base implementation of `_.toPairs` and `_.toPairsIn` which creates an array + * of key-value pairs for `object` corresponding to the property names of `props`. + * + * @private + * @param {Object} object The object to query. + * @param {Array} props The property names to get values for. + * @returns {Object} Returns the key-value pairs. + */ + function baseToPairs(object, props) { + return arrayMap(props, function(key) { + return [key, object[key]]; + }); + } + + /** + * The base implementation of `_.unary` without support for storing metadata. + * + * @private + * @param {Function} func The function to cap arguments for. + * @returns {Function} Returns the new capped function. + */ + function baseUnary(func) { + return function(value) { + return func(value); + }; + } + + /** + * The base implementation of `_.values` and `_.valuesIn` which creates an + * array of `object` property values corresponding to the property names + * of `props`. + * + * @private + * @param {Object} object The object to query. + * @param {Array} props The property names to get values for. + * @returns {Object} Returns the array of property values. + */ + function baseValues(object, props) { + return arrayMap(props, function(key) { + return object[key]; + }); + } + + /** + * Checks if a `cache` value for `key` exists. + * + * @private + * @param {Object} cache The cache to query. + * @param {string} key The key of the entry to check. + * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`. + */ + function cacheHas(cache, key) { + return cache.has(key); + } + + /** + * Used by `_.trim` and `_.trimStart` to get the index of the first string symbol + * that is not found in the character symbols. + * + * @private + * @param {Array} strSymbols The string symbols to inspect. + * @param {Array} chrSymbols The character symbols to find. + * @returns {number} Returns the index of the first unmatched string symbol. + */ + function charsStartIndex(strSymbols, chrSymbols) { + var index = -1, + length = strSymbols.length; + + while (++index < length && baseIndexOf(chrSymbols, strSymbols[index], 0) > -1) {} + return index; + } + + /** + * Used by `_.trim` and `_.trimEnd` to get the index of the last string symbol + * that is not found in the character symbols. + * + * @private + * @param {Array} strSymbols The string symbols to inspect. + * @param {Array} chrSymbols The character symbols to find. + * @returns {number} Returns the index of the last unmatched string symbol. + */ + function charsEndIndex(strSymbols, chrSymbols) { + var index = strSymbols.length; + + while (index-- && baseIndexOf(chrSymbols, strSymbols[index], 0) > -1) {} + return index; + } + + /** + * Gets the number of `placeholder` occurrences in `array`. + * + * @private + * @param {Array} array The array to inspect. + * @param {*} placeholder The placeholder to search for. + * @returns {number} Returns the placeholder count. + */ + function countHolders(array, placeholder) { + var length = array.length, + result = 0; + + while (length--) { + if (array[length] === placeholder) { + ++result; + } + } + return result; + } + + /** + * Used by `_.deburr` to convert Latin-1 Supplement and Latin Extended-A + * letters to basic Latin letters. + * + * @private + * @param {string} letter The matched letter to deburr. + * @returns {string} Returns the deburred letter. + */ + var deburrLetter = basePropertyOf(deburredLetters); + + /** + * Used by `_.escape` to convert characters to HTML entities. + * + * @private + * @param {string} chr The matched character to escape. + * @returns {string} Returns the escaped character. + */ + var escapeHtmlChar = basePropertyOf(htmlEscapes); + + /** + * Used by `_.template` to escape characters for inclusion in compiled string literals. + * + * @private + * @param {string} chr The matched character to escape. + * @returns {string} Returns the escaped character. + */ + function escapeStringChar(chr) { + return '\\' + stringEscapes[chr]; + } + + /** + * Gets the value at `key` of `object`. + * + * @private + * @param {Object} [object] The object to query. + * @param {string} key The key of the property to get. + * @returns {*} Returns the property value. + */ + function getValue(object, key) { + return object == null ? undefined : object[key]; + } + + /** + * Checks if `string` contains Unicode symbols. + * + * @private + * @param {string} string The string to inspect. + * @returns {boolean} Returns `true` if a symbol is found, else `false`. + */ + function hasUnicode(string) { + return reHasUnicode.test(string); + } + + /** + * Checks if `string` contains a word composed of Unicode symbols. + * + * @private + * @param {string} string The string to inspect. + * @returns {boolean} Returns `true` if a word is found, else `false`. + */ + function hasUnicodeWord(string) { + return reHasUnicodeWord.test(string); + } + + /** + * Converts `iterator` to an array. + * + * @private + * @param {Object} iterator The iterator to convert. + * @returns {Array} Returns the converted array. + */ + function iteratorToArray(iterator) { + var data, + result = []; + + while (!(data = iterator.next()).done) { + result.push(data.value); + } + return result; + } + + /** + * Converts `map` to its key-value pairs. + * + * @private + * @param {Object} map The map to convert. + * @returns {Array} Returns the key-value pairs. + */ + function mapToArray(map) { + var index = -1, + result = Array(map.size); + + map.forEach(function(value, key) { + result[++index] = [key, value]; + }); + return result; + } + + /** + * Creates a unary function that invokes `func` with its argument transformed. + * + * @private + * @param {Function} func The function to wrap. + * @param {Function} transform The argument transform. + * @returns {Function} Returns the new function. + */ + function overArg(func, transform) { + return function(arg) { + return func(transform(arg)); + }; + } + + /** + * Replaces all `placeholder` elements in `array` with an internal placeholder + * and returns an array of their indexes. + * + * @private + * @param {Array} array The array to modify. + * @param {*} placeholder The placeholder to replace. + * @returns {Array} Returns the new array of placeholder indexes. + */ + function replaceHolders(array, placeholder) { + var index = -1, + length = array.length, + resIndex = 0, + result = []; + + while (++index < length) { + var value = array[index]; + if (value === placeholder || value === PLACEHOLDER) { + array[index] = PLACEHOLDER; + result[resIndex++] = index; + } + } + return result; + } + + /** + * Converts `set` to an array of its values. + * + * @private + * @param {Object} set The set to convert. + * @returns {Array} Returns the values. + */ + function setToArray(set) { + var index = -1, + result = Array(set.size); + + set.forEach(function(value) { + result[++index] = value; + }); + return result; + } + + /** + * Converts `set` to its value-value pairs. + * + * @private + * @param {Object} set The set to convert. + * @returns {Array} Returns the value-value pairs. + */ + function setToPairs(set) { + var index = -1, + result = Array(set.size); + + set.forEach(function(value) { + result[++index] = [value, value]; + }); + return result; + } + + /** + * A specialized version of `_.indexOf` which performs strict equality + * comparisons of values, i.e. `===`. + * + * @private + * @param {Array} array The array to inspect. + * @param {*} value The value to search for. + * @param {number} fromIndex The index to search from. + * @returns {number} Returns the index of the matched value, else `-1`. + */ + function strictIndexOf(array, value, fromIndex) { + var index = fromIndex - 1, + length = array.length; + + while (++index < length) { + if (array[index] === value) { + return index; + } + } + return -1; + } + + /** + * A specialized version of `_.lastIndexOf` which performs strict equality + * comparisons of values, i.e. `===`. + * + * @private + * @param {Array} array The array to inspect. + * @param {*} value The value to search for. + * @param {number} fromIndex The index to search from. + * @returns {number} Returns the index of the matched value, else `-1`. + */ + function strictLastIndexOf(array, value, fromIndex) { + var index = fromIndex + 1; + while (index--) { + if (array[index] === value) { + return index; + } + } + return index; + } + + /** + * Gets the number of symbols in `string`. + * + * @private + * @param {string} string The string to inspect. + * @returns {number} Returns the string size. + */ + function stringSize(string) { + return hasUnicode(string) + ? unicodeSize(string) + : asciiSize(string); + } + + /** + * Converts `string` to an array. + * + * @private + * @param {string} string The string to convert. + * @returns {Array} Returns the converted array. + */ + function stringToArray(string) { + return hasUnicode(string) + ? unicodeToArray(string) + : asciiToArray(string); + } + + /** + * Used by `_.unescape` to convert HTML entities to characters. + * + * @private + * @param {string} chr The matched character to unescape. + * @returns {string} Returns the unescaped character. + */ + var unescapeHtmlChar = basePropertyOf(htmlUnescapes); + + /** + * Gets the size of a Unicode `string`. + * + * @private + * @param {string} string The string inspect. + * @returns {number} Returns the string size. + */ + function unicodeSize(string) { + var result = reUnicode.lastIndex = 0; + while (reUnicode.test(string)) { + ++result; + } + return result; + } + + /** + * Converts a Unicode `string` to an array. + * + * @private + * @param {string} string The string to convert. + * @returns {Array} Returns the converted array. + */ + function unicodeToArray(string) { + return string.match(reUnicode) || []; + } + + /** + * Splits a Unicode `string` into an array of its words. + * + * @private + * @param {string} The string to inspect. + * @returns {Array} Returns the words of `string`. + */ + function unicodeWords(string) { + return string.match(reUnicodeWord) || []; + } + + /*--------------------------------------------------------------------------*/ + + /** + * Create a new pristine `lodash` function using the `context` object. + * + * @static + * @memberOf _ + * @since 1.1.0 + * @category Util + * @param {Object} [context=root] The context object. + * @returns {Function} Returns a new `lodash` function. + * @example + * + * _.mixin({ 'foo': _.constant('foo') }); + * + * var lodash = _.runInContext(); + * lodash.mixin({ 'bar': lodash.constant('bar') }); + * + * _.isFunction(_.foo); + * // => true + * _.isFunction(_.bar); + * // => false + * + * lodash.isFunction(lodash.foo); + * // => false + * lodash.isFunction(lodash.bar); + * // => true + * + * // Create a suped-up `defer` in Node.js. + * var defer = _.runInContext({ 'setTimeout': setImmediate }).defer; + */ + var runInContext = (function runInContext(context) { + context = context == null ? root : _.defaults(root.Object(), context, _.pick(root, contextProps)); + + /** Built-in constructor references. */ + var Array = context.Array, + Date = context.Date, + Error = context.Error, + Function = context.Function, + Math = context.Math, + Object = context.Object, + RegExp = context.RegExp, + String = context.String, + TypeError = context.TypeError; + + /** Used for built-in method references. */ + var arrayProto = Array.prototype, + funcProto = Function.prototype, + objectProto = Object.prototype; + + /** Used to detect overreaching core-js shims. */ + var coreJsData = context['__core-js_shared__']; + + /** Used to resolve the decompiled source of functions. */ + var funcToString = funcProto.toString; + + /** Used to check objects for own properties. */ + var hasOwnProperty = objectProto.hasOwnProperty; + + /** Used to generate unique IDs. */ + var idCounter = 0; + + /** Used to detect methods masquerading as native. */ + var maskSrcKey = (function() { + var uid = /[^.]+$/.exec(coreJsData && coreJsData.keys && coreJsData.keys.IE_PROTO || ''); + return uid ? ('Symbol(src)_1.' + uid) : ''; + }()); + + /** + * Used to resolve the + * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring) + * of values. + */ + var nativeObjectToString = objectProto.toString; + + /** Used to infer the `Object` constructor. */ + var objectCtorString = funcToString.call(Object); + + /** Used to restore the original `_` reference in `_.noConflict`. */ + var oldDash = root._; + + /** Used to detect if a method is native. */ + var reIsNative = RegExp('^' + + funcToString.call(hasOwnProperty).replace(reRegExpChar, '\\$&') + .replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g, '$1.*?') + '$' + ); + + /** Built-in value references. */ + var Buffer = moduleExports ? context.Buffer : undefined, + Symbol = context.Symbol, + Uint8Array = context.Uint8Array, + allocUnsafe = Buffer ? Buffer.allocUnsafe : undefined, + getPrototype = overArg(Object.getPrototypeOf, Object), + objectCreate = Object.create, + propertyIsEnumerable = objectProto.propertyIsEnumerable, + splice = arrayProto.splice, + spreadableSymbol = Symbol ? Symbol.isConcatSpreadable : undefined, + symIterator = Symbol ? Symbol.iterator : undefined, + symToStringTag = Symbol ? Symbol.toStringTag : undefined; + + var defineProperty = (function() { + try { + var func = getNative(Object, 'defineProperty'); + func({}, '', {}); + return func; + } catch (e) {} + }()); + + /** Mocked built-ins. */ + var ctxClearTimeout = context.clearTimeout !== root.clearTimeout && context.clearTimeout, + ctxNow = Date && Date.now !== root.Date.now && Date.now, + ctxSetTimeout = context.setTimeout !== root.setTimeout && context.setTimeout; + + /* Built-in method references for those with the same name as other `lodash` methods. */ + var nativeCeil = Math.ceil, + nativeFloor = Math.floor, + nativeGetSymbols = Object.getOwnPropertySymbols, + nativeIsBuffer = Buffer ? Buffer.isBuffer : undefined, + nativeIsFinite = context.isFinite, + nativeJoin = arrayProto.join, + nativeKeys = overArg(Object.keys, Object), + nativeMax = Math.max, + nativeMin = Math.min, + nativeNow = Date.now, + nativeParseInt = context.parseInt, + nativeRandom = Math.random, + nativeReverse = arrayProto.reverse; + + /* Built-in method references that are verified to be native. */ + var DataView = getNative(context, 'DataView'), + Map = getNative(context, 'Map'), + Promise = getNative(context, 'Promise'), + Set = getNative(context, 'Set'), + WeakMap = getNative(context, 'WeakMap'), + nativeCreate = getNative(Object, 'create'); + + /** Used to store function metadata. */ + var metaMap = WeakMap && new WeakMap; + + /** Used to lookup unminified function names. */ + var realNames = {}; + + /** Used to detect maps, sets, and weakmaps. */ + var dataViewCtorString = toSource(DataView), + mapCtorString = toSource(Map), + promiseCtorString = toSource(Promise), + setCtorString = toSource(Set), + weakMapCtorString = toSource(WeakMap); + + /** Used to convert symbols to primitives and strings. */ + var symbolProto = Symbol ? Symbol.prototype : undefined, + symbolValueOf = symbolProto ? symbolProto.valueOf : undefined, + symbolToString = symbolProto ? symbolProto.toString : undefined; + + /*------------------------------------------------------------------------*/ + + /** + * Creates a `lodash` object which wraps `value` to enable implicit method + * chain sequences. Methods that operate on and return arrays, collections, + * and functions can be chained together. Methods that retrieve a single value + * or may return a primitive value will automatically end the chain sequence + * and return the unwrapped value. Otherwise, the value must be unwrapped + * with `_#value`. + * + * Explicit chain sequences, which must be unwrapped with `_#value`, may be + * enabled using `_.chain`. + * + * The execution of chained methods is lazy, that is, it's deferred until + * `_#value` is implicitly or explicitly called. + * + * Lazy evaluation allows several methods to support shortcut fusion. + * Shortcut fusion is an optimization to merge iteratee calls; this avoids + * the creation of intermediate arrays and can greatly reduce the number of + * iteratee executions. Sections of a chain sequence qualify for shortcut + * fusion if the section is applied to an array and iteratees accept only + * one argument. The heuristic for whether a section qualifies for shortcut + * fusion is subject to change. + * + * Chaining is supported in custom builds as long as the `_#value` method is + * directly or indirectly included in the build. + * + * In addition to lodash methods, wrappers have `Array` and `String` methods. + * + * The wrapper `Array` methods are: + * `concat`, `join`, `pop`, `push`, `shift`, `sort`, `splice`, and `unshift` + * + * The wrapper `String` methods are: + * `replace` and `split` + * + * The wrapper methods that support shortcut fusion are: + * `at`, `compact`, `drop`, `dropRight`, `dropWhile`, `filter`, `find`, + * `findLast`, `head`, `initial`, `last`, `map`, `reject`, `reverse`, `slice`, + * `tail`, `take`, `takeRight`, `takeRightWhile`, `takeWhile`, and `toArray` + * + * The chainable wrapper methods are: + * `after`, `ary`, `assign`, `assignIn`, `assignInWith`, `assignWith`, `at`, + * `before`, `bind`, `bindAll`, `bindKey`, `castArray`, `chain`, `chunk`, + * `commit`, `compact`, `concat`, `conforms`, `constant`, `countBy`, `create`, + * `curry`, `debounce`, `defaults`, `defaultsDeep`, `defer`, `delay`, + * `difference`, `differenceBy`, `differenceWith`, `drop`, `dropRight`, + * `dropRightWhile`, `dropWhile`, `extend`, `extendWith`, `fill`, `filter`, + * `flatMap`, `flatMapDeep`, `flatMapDepth`, `flatten`, `flattenDeep`, + * `flattenDepth`, `flip`, `flow`, `flowRight`, `fromPairs`, `functions`, + * `functionsIn`, `groupBy`, `initial`, `intersection`, `intersectionBy`, + * `intersectionWith`, `invert`, `invertBy`, `invokeMap`, `iteratee`, `keyBy`, + * `keys`, `keysIn`, `map`, `mapKeys`, `mapValues`, `matches`, `matchesProperty`, + * `memoize`, `merge`, `mergeWith`, `method`, `methodOf`, `mixin`, `negate`, + * `nthArg`, `omit`, `omitBy`, `once`, `orderBy`, `over`, `overArgs`, + * `overEvery`, `overSome`, `partial`, `partialRight`, `partition`, `pick`, + * `pickBy`, `plant`, `property`, `propertyOf`, `pull`, `pullAll`, `pullAllBy`, + * `pullAllWith`, `pullAt`, `push`, `range`, `rangeRight`, `rearg`, `reject`, + * `remove`, `rest`, `reverse`, `sampleSize`, `set`, `setWith`, `shuffle`, + * `slice`, `sort`, `sortBy`, `splice`, `spread`, `tail`, `take`, `takeRight`, + * `takeRightWhile`, `takeWhile`, `tap`, `throttle`, `thru`, `toArray`, + * `toPairs`, `toPairsIn`, `toPath`, `toPlainObject`, `transform`, `unary`, + * `union`, `unionBy`, `unionWith`, `uniq`, `uniqBy`, `uniqWith`, `unset`, + * `unshift`, `unzip`, `unzipWith`, `update`, `updateWith`, `values`, + * `valuesIn`, `without`, `wrap`, `xor`, `xorBy`, `xorWith`, `zip`, + * `zipObject`, `zipObjectDeep`, and `zipWith` + * + * The wrapper methods that are **not** chainable by default are: + * `add`, `attempt`, `camelCase`, `capitalize`, `ceil`, `clamp`, `clone`, + * `cloneDeep`, `cloneDeepWith`, `cloneWith`, `conformsTo`, `deburr`, + * `defaultTo`, `divide`, `each`, `eachRight`, `endsWith`, `eq`, `escape`, + * `escapeRegExp`, `every`, `find`, `findIndex`, `findKey`, `findLast`, + * `findLastIndex`, `findLastKey`, `first`, `floor`, `forEach`, `forEachRight`, + * `forIn`, `forInRight`, `forOwn`, `forOwnRight`, `get`, `gt`, `gte`, `has`, + * `hasIn`, `head`, `identity`, `includes`, `indexOf`, `inRange`, `invoke`, + * `isArguments`, `isArray`, `isArrayBuffer`, `isArrayLike`, `isArrayLikeObject`, + * `isBoolean`, `isBuffer`, `isDate`, `isElement`, `isEmpty`, `isEqual`, + * `isEqualWith`, `isError`, `isFinite`, `isFunction`, `isInteger`, `isLength`, + * `isMap`, `isMatch`, `isMatchWith`, `isNaN`, `isNative`, `isNil`, `isNull`, + * `isNumber`, `isObject`, `isObjectLike`, `isPlainObject`, `isRegExp`, + * `isSafeInteger`, `isSet`, `isString`, `isUndefined`, `isTypedArray`, + * `isWeakMap`, `isWeakSet`, `join`, `kebabCase`, `last`, `lastIndexOf`, + * `lowerCase`, `lowerFirst`, `lt`, `lte`, `max`, `maxBy`, `mean`, `meanBy`, + * `min`, `minBy`, `multiply`, `noConflict`, `noop`, `now`, `nth`, `pad`, + * `padEnd`, `padStart`, `parseInt`, `pop`, `random`, `reduce`, `reduceRight`, + * `repeat`, `result`, `round`, `runInContext`, `sample`, `shift`, `size`, + * `snakeCase`, `some`, `sortedIndex`, `sortedIndexBy`, `sortedLastIndex`, + * `sortedLastIndexBy`, `startCase`, `startsWith`, `stubArray`, `stubFalse`, + * `stubObject`, `stubString`, `stubTrue`, `subtract`, `sum`, `sumBy`, + * `template`, `times`, `toFinite`, `toInteger`, `toJSON`, `toLength`, + * `toLower`, `toNumber`, `toSafeInteger`, `toString`, `toUpper`, `trim`, + * `trimEnd`, `trimStart`, `truncate`, `unescape`, `uniqueId`, `upperCase`, + * `upperFirst`, `value`, and `words` + * + * @name _ + * @constructor + * @category Seq + * @param {*} value The value to wrap in a `lodash` instance. + * @returns {Object} Returns the new `lodash` wrapper instance. + * @example + * + * function square(n) { + * return n * n; + * } + * + * var wrapped = _([1, 2, 3]); + * + * // Returns an unwrapped value. + * wrapped.reduce(_.add); + * // => 6 + * + * // Returns a wrapped value. + * var squares = wrapped.map(square); + * + * _.isArray(squares); + * // => false + * + * _.isArray(squares.value()); + * // => true + */ + function lodash(value) { + if (isObjectLike(value) && !isArray(value) && !(value instanceof LazyWrapper)) { + if (value instanceof LodashWrapper) { + return value; + } + if (hasOwnProperty.call(value, '__wrapped__')) { + return wrapperClone(value); + } + } + return new LodashWrapper(value); + } + + /** + * The base implementation of `_.create` without support for assigning + * properties to the created object. + * + * @private + * @param {Object} proto The object to inherit from. + * @returns {Object} Returns the new object. + */ + var baseCreate = (function() { + function object() {} + return function(proto) { + if (!isObject(proto)) { + return {}; + } + if (objectCreate) { + return objectCreate(proto); + } + object.prototype = proto; + var result = new object; + object.prototype = undefined; + return result; + }; + }()); + + /** + * The function whose prototype chain sequence wrappers inherit from. + * + * @private + */ + function baseLodash() { + // No operation performed. + } + + /** + * The base constructor for creating `lodash` wrapper objects. + * + * @private + * @param {*} value The value to wrap. + * @param {boolean} [chainAll] Enable explicit method chain sequences. + */ + function LodashWrapper(value, chainAll) { + this.__wrapped__ = value; + this.__actions__ = []; + this.__chain__ = !!chainAll; + this.__index__ = 0; + this.__values__ = undefined; + } + + /** + * By default, the template delimiters used by lodash are like those in + * embedded Ruby (ERB) as well as ES2015 template strings. Change the + * following template settings to use alternative delimiters. + * + * @static + * @memberOf _ + * @type {Object} + */ + lodash.templateSettings = { + + /** + * Used to detect `data` property values to be HTML-escaped. + * + * @memberOf _.templateSettings + * @type {RegExp} + */ + 'escape': reEscape, + + /** + * Used to detect code to be evaluated. + * + * @memberOf _.templateSettings + * @type {RegExp} + */ + 'evaluate': reEvaluate, + + /** + * Used to detect `data` property values to inject. + * + * @memberOf _.templateSettings + * @type {RegExp} + */ + 'interpolate': reInterpolate, + + /** + * Used to reference the data object in the template text. + * + * @memberOf _.templateSettings + * @type {string} + */ + 'variable': '', + + /** + * Used to import variables into the compiled template. + * + * @memberOf _.templateSettings + * @type {Object} + */ + 'imports': { + + /** + * A reference to the `lodash` function. + * + * @memberOf _.templateSettings.imports + * @type {Function} + */ + '_': lodash + } + }; + + // Ensure wrappers are instances of `baseLodash`. + lodash.prototype = baseLodash.prototype; + lodash.prototype.constructor = lodash; + + LodashWrapper.prototype = baseCreate(baseLodash.prototype); + LodashWrapper.prototype.constructor = LodashWrapper; + + /*------------------------------------------------------------------------*/ + + /** + * Creates a lazy wrapper object which wraps `value` to enable lazy evaluation. + * + * @private + * @constructor + * @param {*} value The value to wrap. + */ + function LazyWrapper(value) { + this.__wrapped__ = value; + this.__actions__ = []; + this.__dir__ = 1; + this.__filtered__ = false; + this.__iteratees__ = []; + this.__takeCount__ = MAX_ARRAY_LENGTH; + this.__views__ = []; + } + + /** + * Creates a clone of the lazy wrapper object. + * + * @private + * @name clone + * @memberOf LazyWrapper + * @returns {Object} Returns the cloned `LazyWrapper` object. + */ + function lazyClone() { + var result = new LazyWrapper(this.__wrapped__); + result.__actions__ = copyArray(this.__actions__); + result.__dir__ = this.__dir__; + result.__filtered__ = this.__filtered__; + result.__iteratees__ = copyArray(this.__iteratees__); + result.__takeCount__ = this.__takeCount__; + result.__views__ = copyArray(this.__views__); + return result; + } + + /** + * Reverses the direction of lazy iteration. + * + * @private + * @name reverse + * @memberOf LazyWrapper + * @returns {Object} Returns the new reversed `LazyWrapper` object. + */ + function lazyReverse() { + if (this.__filtered__) { + var result = new LazyWrapper(this); + result.__dir__ = -1; + result.__filtered__ = true; + } else { + result = this.clone(); + result.__dir__ *= -1; + } + return result; + } + + /** + * Extracts the unwrapped value from its lazy wrapper. + * + * @private + * @name value + * @memberOf LazyWrapper + * @returns {*} Returns the unwrapped value. + */ + function lazyValue() { + var array = this.__wrapped__.value(), + dir = this.__dir__, + isArr = isArray(array), + isRight = dir < 0, + arrLength = isArr ? array.length : 0, + view = getView(0, arrLength, this.__views__), + start = view.start, + end = view.end, + length = end - start, + index = isRight ? end : (start - 1), + iteratees = this.__iteratees__, + iterLength = iteratees.length, + resIndex = 0, + takeCount = nativeMin(length, this.__takeCount__); + + if (!isArr || (!isRight && arrLength == length && takeCount == length)) { + return baseWrapperValue(array, this.__actions__); + } + var result = []; + + outer: + while (length-- && resIndex < takeCount) { + index += dir; + + var iterIndex = -1, + value = array[index]; + + while (++iterIndex < iterLength) { + var data = iteratees[iterIndex], + iteratee = data.iteratee, + type = data.type, + computed = iteratee(value); + + if (type == LAZY_MAP_FLAG) { + value = computed; + } else if (!computed) { + if (type == LAZY_FILTER_FLAG) { + continue outer; + } else { + break outer; + } + } + } + result[resIndex++] = value; + } + return result; + } + + // Ensure `LazyWrapper` is an instance of `baseLodash`. + LazyWrapper.prototype = baseCreate(baseLodash.prototype); + LazyWrapper.prototype.constructor = LazyWrapper; + + /*------------------------------------------------------------------------*/ + + /** + * Creates a hash object. + * + * @private + * @constructor + * @param {Array} [entries] The key-value pairs to cache. + */ + function Hash(entries) { + var index = -1, + length = entries == null ? 0 : entries.length; + + this.clear(); + while (++index < length) { + var entry = entries[index]; + this.set(entry[0], entry[1]); + } + } + + /** + * Removes all key-value entries from the hash. + * + * @private + * @name clear + * @memberOf Hash + */ + function hashClear() { + this.__data__ = nativeCreate ? nativeCreate(null) : {}; + this.size = 0; + } + + /** + * Removes `key` and its value from the hash. + * + * @private + * @name delete + * @memberOf Hash + * @param {Object} hash The hash to modify. + * @param {string} key The key of the value to remove. + * @returns {boolean} Returns `true` if the entry was removed, else `false`. + */ + function hashDelete(key) { + var result = this.has(key) && delete this.__data__[key]; + this.size -= result ? 1 : 0; + return result; + } + + /** + * Gets the hash value for `key`. + * + * @private + * @name get + * @memberOf Hash + * @param {string} key The key of the value to get. + * @returns {*} Returns the entry value. + */ + function hashGet(key) { + var data = this.__data__; + if (nativeCreate) { + var result = data[key]; + return result === HASH_UNDEFINED ? undefined : result; + } + return hasOwnProperty.call(data, key) ? data[key] : undefined; + } + + /** + * Checks if a hash value for `key` exists. + * + * @private + * @name has + * @memberOf Hash + * @param {string} key The key of the entry to check. + * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`. + */ + function hashHas(key) { + var data = this.__data__; + return nativeCreate ? (data[key] !== undefined) : hasOwnProperty.call(data, key); + } + + /** + * Sets the hash `key` to `value`. + * + * @private + * @name set + * @memberOf Hash + * @param {string} key The key of the value to set. + * @param {*} value The value to set. + * @returns {Object} Returns the hash instance. + */ + function hashSet(key, value) { + var data = this.__data__; + this.size += this.has(key) ? 0 : 1; + data[key] = (nativeCreate && value === undefined) ? HASH_UNDEFINED : value; + return this; + } + + // Add methods to `Hash`. + Hash.prototype.clear = hashClear; + Hash.prototype['delete'] = hashDelete; + Hash.prototype.get = hashGet; + Hash.prototype.has = hashHas; + Hash.prototype.set = hashSet; + + /*------------------------------------------------------------------------*/ + + /** + * Creates an list cache object. + * + * @private + * @constructor + * @param {Array} [entries] The key-value pairs to cache. + */ + function ListCache(entries) { + var index = -1, + length = entries == null ? 0 : entries.length; + + this.clear(); + while (++index < length) { + var entry = entries[index]; + this.set(entry[0], entry[1]); + } + } + + /** + * Removes all key-value entries from the list cache. + * + * @private + * @name clear + * @memberOf ListCache + */ + function listCacheClear() { + this.__data__ = []; + this.size = 0; + } + + /** + * Removes `key` and its value from the list cache. + * + * @private + * @name delete + * @memberOf ListCache + * @param {string} key The key of the value to remove. + * @returns {boolean} Returns `true` if the entry was removed, else `false`. + */ + function listCacheDelete(key) { + var data = this.__data__, + index = assocIndexOf(data, key); + + if (index < 0) { + return false; + } + var lastIndex = data.length - 1; + if (index == lastIndex) { + data.pop(); + } else { + splice.call(data, index, 1); + } + --this.size; + return true; + } + + /** + * Gets the list cache value for `key`. + * + * @private + * @name get + * @memberOf ListCache + * @param {string} key The key of the value to get. + * @returns {*} Returns the entry value. + */ + function listCacheGet(key) { + var data = this.__data__, + index = assocIndexOf(data, key); + + return index < 0 ? undefined : data[index][1]; + } + + /** + * Checks if a list cache value for `key` exists. + * + * @private + * @name has + * @memberOf ListCache + * @param {string} key The key of the entry to check. + * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`. + */ + function listCacheHas(key) { + return assocIndexOf(this.__data__, key) > -1; + } + + /** + * Sets the list cache `key` to `value`. + * + * @private + * @name set + * @memberOf ListCache + * @param {string} key The key of the value to set. + * @param {*} value The value to set. + * @returns {Object} Returns the list cache instance. + */ + function listCacheSet(key, value) { + var data = this.__data__, + index = assocIndexOf(data, key); + + if (index < 0) { + ++this.size; + data.push([key, value]); + } else { + data[index][1] = value; + } + return this; + } + + // Add methods to `ListCache`. + ListCache.prototype.clear = listCacheClear; + ListCache.prototype['delete'] = listCacheDelete; + ListCache.prototype.get = listCacheGet; + ListCache.prototype.has = listCacheHas; + ListCache.prototype.set = listCacheSet; + + /*------------------------------------------------------------------------*/ + + /** + * Creates a map cache object to store key-value pairs. + * + * @private + * @constructor + * @param {Array} [entries] The key-value pairs to cache. + */ + function MapCache(entries) { + var index = -1, + length = entries == null ? 0 : entries.length; + + this.clear(); + while (++index < length) { + var entry = entries[index]; + this.set(entry[0], entry[1]); + } + } + + /** + * Removes all key-value entries from the map. + * + * @private + * @name clear + * @memberOf MapCache + */ + function mapCacheClear() { + this.size = 0; + this.__data__ = { + 'hash': new Hash, + 'map': new (Map || ListCache), + 'string': new Hash + }; + } + + /** + * Removes `key` and its value from the map. + * + * @private + * @name delete + * @memberOf MapCache + * @param {string} key The key of the value to remove. + * @returns {boolean} Returns `true` if the entry was removed, else `false`. + */ + function mapCacheDelete(key) { + var result = getMapData(this, key)['delete'](key); + this.size -= result ? 1 : 0; + return result; + } + + /** + * Gets the map value for `key`. + * + * @private + * @name get + * @memberOf MapCache + * @param {string} key The key of the value to get. + * @returns {*} Returns the entry value. + */ + function mapCacheGet(key) { + return getMapData(this, key).get(key); + } + + /** + * Checks if a map value for `key` exists. + * + * @private + * @name has + * @memberOf MapCache + * @param {string} key The key of the entry to check. + * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`. + */ + function mapCacheHas(key) { + return getMapData(this, key).has(key); + } + + /** + * Sets the map `key` to `value`. + * + * @private + * @name set + * @memberOf MapCache + * @param {string} key The key of the value to set. + * @param {*} value The value to set. + * @returns {Object} Returns the map cache instance. + */ + function mapCacheSet(key, value) { + var data = getMapData(this, key), + size = data.size; + + data.set(key, value); + this.size += data.size == size ? 0 : 1; + return this; + } + + // Add methods to `MapCache`. + MapCache.prototype.clear = mapCacheClear; + MapCache.prototype['delete'] = mapCacheDelete; + MapCache.prototype.get = mapCacheGet; + MapCache.prototype.has = mapCacheHas; + MapCache.prototype.set = mapCacheSet; + + /*------------------------------------------------------------------------*/ + + /** + * + * Creates an array cache object to store unique values. + * + * @private + * @constructor + * @param {Array} [values] The values to cache. + */ + function SetCache(values) { + var index = -1, + length = values == null ? 0 : values.length; + + this.__data__ = new MapCache; + while (++index < length) { + this.add(values[index]); + } + } + + /** + * Adds `value` to the array cache. + * + * @private + * @name add + * @memberOf SetCache + * @alias push + * @param {*} value The value to cache. + * @returns {Object} Returns the cache instance. + */ + function setCacheAdd(value) { + this.__data__.set(value, HASH_UNDEFINED); + return this; + } + + /** + * Checks if `value` is in the array cache. + * + * @private + * @name has + * @memberOf SetCache + * @param {*} value The value to search for. + * @returns {number} Returns `true` if `value` is found, else `false`. + */ + function setCacheHas(value) { + return this.__data__.has(value); + } + + // Add methods to `SetCache`. + SetCache.prototype.add = SetCache.prototype.push = setCacheAdd; + SetCache.prototype.has = setCacheHas; + + /*------------------------------------------------------------------------*/ + + /** + * Creates a stack cache object to store key-value pairs. + * + * @private + * @constructor + * @param {Array} [entries] The key-value pairs to cache. + */ + function Stack(entries) { + var data = this.__data__ = new ListCache(entries); + this.size = data.size; + } + + /** + * Removes all key-value entries from the stack. + * + * @private + * @name clear + * @memberOf Stack + */ + function stackClear() { + this.__data__ = new ListCache; + this.size = 0; + } + + /** + * Removes `key` and its value from the stack. + * + * @private + * @name delete + * @memberOf Stack + * @param {string} key The key of the value to remove. + * @returns {boolean} Returns `true` if the entry was removed, else `false`. + */ + function stackDelete(key) { + var data = this.__data__, + result = data['delete'](key); + + this.size = data.size; + return result; + } + + /** + * Gets the stack value for `key`. + * + * @private + * @name get + * @memberOf Stack + * @param {string} key The key of the value to get. + * @returns {*} Returns the entry value. + */ + function stackGet(key) { + return this.__data__.get(key); + } + + /** + * Checks if a stack value for `key` exists. + * + * @private + * @name has + * @memberOf Stack + * @param {string} key The key of the entry to check. + * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`. + */ + function stackHas(key) { + return this.__data__.has(key); + } + + /** + * Sets the stack `key` to `value`. + * + * @private + * @name set + * @memberOf Stack + * @param {string} key The key of the value to set. + * @param {*} value The value to set. + * @returns {Object} Returns the stack cache instance. + */ + function stackSet(key, value) { + var data = this.__data__; + if (data instanceof ListCache) { + var pairs = data.__data__; + if (!Map || (pairs.length < LARGE_ARRAY_SIZE - 1)) { + pairs.push([key, value]); + this.size = ++data.size; + return this; + } + data = this.__data__ = new MapCache(pairs); + } + data.set(key, value); + this.size = data.size; + return this; + } + + // Add methods to `Stack`. + Stack.prototype.clear = stackClear; + Stack.prototype['delete'] = stackDelete; + Stack.prototype.get = stackGet; + Stack.prototype.has = stackHas; + Stack.prototype.set = stackSet; + + /*------------------------------------------------------------------------*/ + + /** + * Creates an array of the enumerable property names of the array-like `value`. + * + * @private + * @param {*} value The value to query. + * @param {boolean} inherited Specify returning inherited property names. + * @returns {Array} Returns the array of property names. + */ + function arrayLikeKeys(value, inherited) { + var isArr = isArray(value), + isArg = !isArr && isArguments(value), + isBuff = !isArr && !isArg && isBuffer(value), + isType = !isArr && !isArg && !isBuff && isTypedArray(value), + skipIndexes = isArr || isArg || isBuff || isType, + result = skipIndexes ? baseTimes(value.length, String) : [], + length = result.length; + + for (var key in value) { + if ((inherited || hasOwnProperty.call(value, key)) && + !(skipIndexes && ( + // Safari 9 has enumerable `arguments.length` in strict mode. + key == 'length' || + // Node.js 0.10 has enumerable non-index properties on buffers. + (isBuff && (key == 'offset' || key == 'parent')) || + // PhantomJS 2 has enumerable non-index properties on typed arrays. + (isType && (key == 'buffer' || key == 'byteLength' || key == 'byteOffset')) || + // Skip index properties. + isIndex(key, length) + ))) { + result.push(key); + } + } + return result; + } + + /** + * A specialized version of `_.sample` for arrays. + * + * @private + * @param {Array} array The array to sample. + * @returns {*} Returns the random element. + */ + function arraySample(array) { + var length = array.length; + return length ? array[baseRandom(0, length - 1)] : undefined; + } + + /** + * A specialized version of `_.sampleSize` for arrays. + * + * @private + * @param {Array} array The array to sample. + * @param {number} n The number of elements to sample. + * @returns {Array} Returns the random elements. + */ + function arraySampleSize(array, n) { + return shuffleSelf(copyArray(array), baseClamp(n, 0, array.length)); + } + + /** + * A specialized version of `_.shuffle` for arrays. + * + * @private + * @param {Array} array The array to shuffle. + * @returns {Array} Returns the new shuffled array. + */ + function arrayShuffle(array) { + return shuffleSelf(copyArray(array)); + } + + /** + * This function is like `assignValue` except that it doesn't assign + * `undefined` values. + * + * @private + * @param {Object} object The object to modify. + * @param {string} key The key of the property to assign. + * @param {*} value The value to assign. + */ + function assignMergeValue(object, key, value) { + if ((value !== undefined && !eq(object[key], value)) || + (value === undefined && !(key in object))) { + baseAssignValue(object, key, value); + } + } + + /** + * Assigns `value` to `key` of `object` if the existing value is not equivalent + * using [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero) + * for equality comparisons. + * + * @private + * @param {Object} object The object to modify. + * @param {string} key The key of the property to assign. + * @param {*} value The value to assign. + */ + function assignValue(object, key, value) { + var objValue = object[key]; + if (!(hasOwnProperty.call(object, key) && eq(objValue, value)) || + (value === undefined && !(key in object))) { + baseAssignValue(object, key, value); + } + } + + /** + * Gets the index at which the `key` is found in `array` of key-value pairs. + * + * @private + * @param {Array} array The array to inspect. + * @param {*} key The key to search for. + * @returns {number} Returns the index of the matched value, else `-1`. + */ + function assocIndexOf(array, key) { + var length = array.length; + while (length--) { + if (eq(array[length][0], key)) { + return length; + } + } + return -1; + } + + /** + * Aggregates elements of `collection` on `accumulator` with keys transformed + * by `iteratee` and values set by `setter`. + * + * @private + * @param {Array|Object} collection The collection to iterate over. + * @param {Function} setter The function to set `accumulator` values. + * @param {Function} iteratee The iteratee to transform keys. + * @param {Object} accumulator The initial aggregated object. + * @returns {Function} Returns `accumulator`. + */ + function baseAggregator(collection, setter, iteratee, accumulator) { + baseEach(collection, function(value, key, collection) { + setter(accumulator, value, iteratee(value), collection); + }); + return accumulator; + } + + /** + * The base implementation of `_.assign` without support for multiple sources + * or `customizer` functions. + * + * @private + * @param {Object} object The destination object. + * @param {Object} source The source object. + * @returns {Object} Returns `object`. + */ + function baseAssign(object, source) { + return object && copyObject(source, keys(source), object); + } + + /** + * The base implementation of `_.assignIn` without support for multiple sources + * or `customizer` functions. + * + * @private + * @param {Object} object The destination object. + * @param {Object} source The source object. + * @returns {Object} Returns `object`. + */ + function baseAssignIn(object, source) { + return object && copyObject(source, keysIn(source), object); + } + + /** + * The base implementation of `assignValue` and `assignMergeValue` without + * value checks. + * + * @private + * @param {Object} object The object to modify. + * @param {string} key The key of the property to assign. + * @param {*} value The value to assign. + */ + function baseAssignValue(object, key, value) { + if (key == '__proto__' && defineProperty) { + defineProperty(object, key, { + 'configurable': true, + 'enumerable': true, + 'value': value, + 'writable': true + }); + } else { + object[key] = value; + } + } + + /** + * The base implementation of `_.at` without support for individual paths. + * + * @private + * @param {Object} object The object to iterate over. + * @param {string[]} paths The property paths to pick. + * @returns {Array} Returns the picked elements. + */ + function baseAt(object, paths) { + var index = -1, + length = paths.length, + result = Array(length), + skip = object == null; + + while (++index < length) { + result[index] = skip ? undefined : get(object, paths[index]); + } + return result; + } + + /** + * The base implementation of `_.clamp` which doesn't coerce arguments. + * + * @private + * @param {number} number The number to clamp. + * @param {number} [lower] The lower bound. + * @param {number} upper The upper bound. + * @returns {number} Returns the clamped number. + */ + function baseClamp(number, lower, upper) { + if (number === number) { + if (upper !== undefined) { + number = number <= upper ? number : upper; + } + if (lower !== undefined) { + number = number >= lower ? number : lower; + } + } + return number; + } + + /** + * The base implementation of `_.clone` and `_.cloneDeep` which tracks + * traversed objects. + * + * @private + * @param {*} value The value to clone. + * @param {boolean} bitmask The bitmask flags. + * 1 - Deep clone + * 2 - Flatten inherited properties + * 4 - Clone symbols + * @param {Function} [customizer] The function to customize cloning. + * @param {string} [key] The key of `value`. + * @param {Object} [object] The parent object of `value`. + * @param {Object} [stack] Tracks traversed objects and their clone counterparts. + * @returns {*} Returns the cloned value. + */ + function baseClone(value, bitmask, customizer, key, object, stack) { + var result, + isDeep = bitmask & CLONE_DEEP_FLAG, + isFlat = bitmask & CLONE_FLAT_FLAG, + isFull = bitmask & CLONE_SYMBOLS_FLAG; + + if (customizer) { + result = object ? customizer(value, key, object, stack) : customizer(value); + } + if (result !== undefined) { + return result; + } + if (!isObject(value)) { + return value; + } + var isArr = isArray(value); + if (isArr) { + result = initCloneArray(value); + if (!isDeep) { + return copyArray(value, result); + } + } else { + var tag = getTag(value), + isFunc = tag == funcTag || tag == genTag; + + if (isBuffer(value)) { + return cloneBuffer(value, isDeep); + } + if (tag == objectTag || tag == argsTag || (isFunc && !object)) { + result = (isFlat || isFunc) ? {} : initCloneObject(value); + if (!isDeep) { + return isFlat + ? copySymbolsIn(value, baseAssignIn(result, value)) + : copySymbols(value, baseAssign(result, value)); + } + } else { + if (!cloneableTags[tag]) { + return object ? value : {}; + } + result = initCloneByTag(value, tag, baseClone, isDeep); + } + } + // Check for circular references and return its corresponding clone. + stack || (stack = new Stack); + var stacked = stack.get(value); + if (stacked) { + return stacked; + } + stack.set(value, result); + + var keysFunc = isFull + ? (isFlat ? getAllKeysIn : getAllKeys) + : (isFlat ? keysIn : keys); + + var props = isArr ? undefined : keysFunc(value); + arrayEach(props || value, function(subValue, key) { + if (props) { + key = subValue; + subValue = value[key]; + } + // Recursively populate clone (susceptible to call stack limits). + assignValue(result, key, baseClone(subValue, bitmask, customizer, key, value, stack)); + }); + return result; + } + + /** + * The base implementation of `_.conforms` which doesn't clone `source`. + * + * @private + * @param {Object} source The object of property predicates to conform to. + * @returns {Function} Returns the new spec function. + */ + function baseConforms(source) { + var props = keys(source); + return function(object) { + return baseConformsTo(object, source, props); + }; + } + + /** + * The base implementation of `_.conformsTo` which accepts `props` to check. + * + * @private + * @param {Object} object The object to inspect. + * @param {Object} source The object of property predicates to conform to. + * @returns {boolean} Returns `true` if `object` conforms, else `false`. + */ + function baseConformsTo(object, source, props) { + var length = props.length; + if (object == null) { + return !length; + } + object = Object(object); + while (length--) { + var key = props[length], + predicate = source[key], + value = object[key]; + + if ((value === undefined && !(key in object)) || !predicate(value)) { + return false; + } + } + return true; + } + + /** + * The base implementation of `_.delay` and `_.defer` which accepts `args` + * to provide to `func`. + * + * @private + * @param {Function} func The function to delay. + * @param {number} wait The number of milliseconds to delay invocation. + * @param {Array} args The arguments to provide to `func`. + * @returns {number|Object} Returns the timer id or timeout object. + */ + function baseDelay(func, wait, args) { + if (typeof func != 'function') { + throw new TypeError(FUNC_ERROR_TEXT); + } + return setTimeout(function() { func.apply(undefined, args); }, wait); + } + + /** + * The base implementation of methods like `_.difference` without support + * for excluding multiple arrays or iteratee shorthands. + * + * @private + * @param {Array} array The array to inspect. + * @param {Array} values The values to exclude. + * @param {Function} [iteratee] The iteratee invoked per element. + * @param {Function} [comparator] The comparator invoked per element. + * @returns {Array} Returns the new array of filtered values. + */ + function baseDifference(array, values, iteratee, comparator) { + var index = -1, + includes = arrayIncludes, + isCommon = true, + length = array.length, + result = [], + valuesLength = values.length; + + if (!length) { + return result; + } + if (iteratee) { + values = arrayMap(values, baseUnary(iteratee)); + } + if (comparator) { + includes = arrayIncludesWith; + isCommon = false; + } + else if (values.length >= LARGE_ARRAY_SIZE) { + includes = cacheHas; + isCommon = false; + values = new SetCache(values); + } + outer: + while (++index < length) { + var value = array[index], + computed = iteratee == null ? value : iteratee(value); + + value = (comparator || value !== 0) ? value : 0; + if (isCommon && computed === computed) { + var valuesIndex = valuesLength; + while (valuesIndex--) { + if (values[valuesIndex] === computed) { + continue outer; + } + } + result.push(value); + } + else if (!includes(values, computed, comparator)) { + result.push(value); + } + } + return result; + } + + /** + * The base implementation of `_.forEach` without support for iteratee shorthands. + * + * @private + * @param {Array|Object} collection The collection to iterate over. + * @param {Function} iteratee The function invoked per iteration. + * @returns {Array|Object} Returns `collection`. + */ + var baseEach = createBaseEach(baseForOwn); + + /** + * The base implementation of `_.forEachRight` without support for iteratee shorthands. + * + * @private + * @param {Array|Object} collection The collection to iterate over. + * @param {Function} iteratee The function invoked per iteration. + * @returns {Array|Object} Returns `collection`. + */ + var baseEachRight = createBaseEach(baseForOwnRight, true); + + /** + * The base implementation of `_.every` without support for iteratee shorthands. + * + * @private + * @param {Array|Object} collection The collection to iterate over. + * @param {Function} predicate The function invoked per iteration. + * @returns {boolean} Returns `true` if all elements pass the predicate check, + * else `false` + */ + function baseEvery(collection, predicate) { + var result = true; + baseEach(collection, function(value, index, collection) { + result = !!predicate(value, index, collection); + return result; + }); + return result; + } + + /** + * The base implementation of methods like `_.max` and `_.min` which accepts a + * `comparator` to determine the extremum value. + * + * @private + * @param {Array} array The array to iterate over. + * @param {Function} iteratee The iteratee invoked per iteration. + * @param {Function} comparator The comparator used to compare values. + * @returns {*} Returns the extremum value. + */ + function baseExtremum(array, iteratee, comparator) { + var index = -1, + length = array.length; + + while (++index < length) { + var value = array[index], + current = iteratee(value); + + if (current != null && (computed === undefined + ? (current === current && !isSymbol(current)) + : comparator(current, computed) + )) { + var computed = current, + result = value; + } + } + return result; + } + + /** + * The base implementation of `_.fill` without an iteratee call guard. + * + * @private + * @param {Array} array The array to fill. + * @param {*} value The value to fill `array` with. + * @param {number} [start=0] The start position. + * @param {number} [end=array.length] The end position. + * @returns {Array} Returns `array`. + */ + function baseFill(array, value, start, end) { + var length = array.length; + + start = toInteger(start); + if (start < 0) { + start = -start > length ? 0 : (length + start); + } + end = (end === undefined || end > length) ? length : toInteger(end); + if (end < 0) { + end += length; + } + end = start > end ? 0 : toLength(end); + while (start < end) { + array[start++] = value; + } + return array; + } + + /** + * The base implementation of `_.filter` without support for iteratee shorthands. + * + * @private + * @param {Array|Object} collection The collection to iterate over. + * @param {Function} predicate The function invoked per iteration. + * @returns {Array} Returns the new filtered array. + */ + function baseFilter(collection, predicate) { + var result = []; + baseEach(collection, function(value, index, collection) { + if (predicate(value, index, collection)) { + result.push(value); + } + }); + return result; + } + + /** + * The base implementation of `_.flatten` with support for restricting flattening. + * + * @private + * @param {Array} array The array to flatten. + * @param {number} depth The maximum recursion depth. + * @param {boolean} [predicate=isFlattenable] The function invoked per iteration. + * @param {boolean} [isStrict] Restrict to values that pass `predicate` checks. + * @param {Array} [result=[]] The initial result value. + * @returns {Array} Returns the new flattened array. + */ + function baseFlatten(array, depth, predicate, isStrict, result) { + var index = -1, + length = array.length; + + predicate || (predicate = isFlattenable); + result || (result = []); + + while (++index < length) { + var value = array[index]; + if (depth > 0 && predicate(value)) { + if (depth > 1) { + // Recursively flatten arrays (susceptible to call stack limits). + baseFlatten(value, depth - 1, predicate, isStrict, result); + } else { + arrayPush(result, value); + } + } else if (!isStrict) { + result[result.length] = value; + } + } + return result; + } + + /** + * The base implementation of `baseForOwn` which iterates over `object` + * properties returned by `keysFunc` and invokes `iteratee` for each property. + * Iteratee functions may exit iteration early by explicitly returning `false`. + * + * @private + * @param {Object} object The object to iterate over. + * @param {Function} iteratee The function invoked per iteration. + * @param {Function} keysFunc The function to get the keys of `object`. + * @returns {Object} Returns `object`. + */ + var baseFor = createBaseFor(); + + /** + * This function is like `baseFor` except that it iterates over properties + * in the opposite order. + * + * @private + * @param {Object} object The object to iterate over. + * @param {Function} iteratee The function invoked per iteration. + * @param {Function} keysFunc The function to get the keys of `object`. + * @returns {Object} Returns `object`. + */ + var baseForRight = createBaseFor(true); + + /** + * The base implementation of `_.forOwn` without support for iteratee shorthands. + * + * @private + * @param {Object} object The object to iterate over. + * @param {Function} iteratee The function invoked per iteration. + * @returns {Object} Returns `object`. + */ + function baseForOwn(object, iteratee) { + return object && baseFor(object, iteratee, keys); + } + + /** + * The base implementation of `_.forOwnRight` without support for iteratee shorthands. + * + * @private + * @param {Object} object The object to iterate over. + * @param {Function} iteratee The function invoked per iteration. + * @returns {Object} Returns `object`. + */ + function baseForOwnRight(object, iteratee) { + return object && baseForRight(object, iteratee, keys); + } + + /** + * The base implementation of `_.functions` which creates an array of + * `object` function property names filtered from `props`. + * + * @private + * @param {Object} object The object to inspect. + * @param {Array} props The property names to filter. + * @returns {Array} Returns the function names. + */ + function baseFunctions(object, props) { + return arrayFilter(props, function(key) { + return isFunction(object[key]); + }); + } + + /** + * The base implementation of `_.get` without support for default values. + * + * @private + * @param {Object} object The object to query. + * @param {Array|string} path The path of the property to get. + * @returns {*} Returns the resolved value. + */ + function baseGet(object, path) { + path = castPath(path, object); + + var index = 0, + length = path.length; + + while (object != null && index < length) { + object = object[toKey(path[index++])]; + } + return (index && index == length) ? object : undefined; + } + + /** + * The base implementation of `getAllKeys` and `getAllKeysIn` which uses + * `keysFunc` and `symbolsFunc` to get the enumerable property names and + * symbols of `object`. + * + * @private + * @param {Object} object The object to query. + * @param {Function} keysFunc The function to get the keys of `object`. + * @param {Function} symbolsFunc The function to get the symbols of `object`. + * @returns {Array} Returns the array of property names and symbols. + */ + function baseGetAllKeys(object, keysFunc, symbolsFunc) { + var result = keysFunc(object); + return isArray(object) ? result : arrayPush(result, symbolsFunc(object)); + } + + /** + * The base implementation of `getTag` without fallbacks for buggy environments. + * + * @private + * @param {*} value The value to query. + * @returns {string} Returns the `toStringTag`. + */ + function baseGetTag(value) { + if (value == null) { + return value === undefined ? undefinedTag : nullTag; + } + return (symToStringTag && symToStringTag in Object(value)) + ? getRawTag(value) + : objectToString(value); + } + + /** + * The base implementation of `_.gt` which doesn't coerce arguments. + * + * @private + * @param {*} value The value to compare. + * @param {*} other The other value to compare. + * @returns {boolean} Returns `true` if `value` is greater than `other`, + * else `false`. + */ + function baseGt(value, other) { + return value > other; + } + + /** + * The base implementation of `_.has` without support for deep paths. + * + * @private + * @param {Object} [object] The object to query. + * @param {Array|string} key The key to check. + * @returns {boolean} Returns `true` if `key` exists, else `false`. + */ + function baseHas(object, key) { + return object != null && hasOwnProperty.call(object, key); + } + + /** + * The base implementation of `_.hasIn` without support for deep paths. + * + * @private + * @param {Object} [object] The object to query. + * @param {Array|string} key The key to check. + * @returns {boolean} Returns `true` if `key` exists, else `false`. + */ + function baseHasIn(object, key) { + return object != null && key in Object(object); + } + + /** + * The base implementation of `_.inRange` which doesn't coerce arguments. + * + * @private + * @param {number} number The number to check. + * @param {number} start The start of the range. + * @param {number} end The end of the range. + * @returns {boolean} Returns `true` if `number` is in the range, else `false`. + */ + function baseInRange(number, start, end) { + return number >= nativeMin(start, end) && number < nativeMax(start, end); + } + + /** + * The base implementation of methods like `_.intersection`, without support + * for iteratee shorthands, that accepts an array of arrays to inspect. + * + * @private + * @param {Array} arrays The arrays to inspect. + * @param {Function} [iteratee] The iteratee invoked per element. + * @param {Function} [comparator] The comparator invoked per element. + * @returns {Array} Returns the new array of shared values. + */ + function baseIntersection(arrays, iteratee, comparator) { + var includes = comparator ? arrayIncludesWith : arrayIncludes, + length = arrays[0].length, + othLength = arrays.length, + othIndex = othLength, + caches = Array(othLength), + maxLength = Infinity, + result = []; + + while (othIndex--) { + var array = arrays[othIndex]; + if (othIndex && iteratee) { + array = arrayMap(array, baseUnary(iteratee)); + } + maxLength = nativeMin(array.length, maxLength); + caches[othIndex] = !comparator && (iteratee || (length >= 120 && array.length >= 120)) + ? new SetCache(othIndex && array) + : undefined; + } + array = arrays[0]; + + var index = -1, + seen = caches[0]; + + outer: + while (++index < length && result.length < maxLength) { + var value = array[index], + computed = iteratee ? iteratee(value) : value; + + value = (comparator || value !== 0) ? value : 0; + if (!(seen + ? cacheHas(seen, computed) + : includes(result, computed, comparator) + )) { + othIndex = othLength; + while (--othIndex) { + var cache = caches[othIndex]; + if (!(cache + ? cacheHas(cache, computed) + : includes(arrays[othIndex], computed, comparator)) + ) { + continue outer; + } + } + if (seen) { + seen.push(computed); + } + result.push(value); + } + } + return result; + } + + /** + * The base implementation of `_.invert` and `_.invertBy` which inverts + * `object` with values transformed by `iteratee` and set by `setter`. + * + * @private + * @param {Object} object The object to iterate over. + * @param {Function} setter The function to set `accumulator` values. + * @param {Function} iteratee The iteratee to transform values. + * @param {Object} accumulator The initial inverted object. + * @returns {Function} Returns `accumulator`. + */ + function baseInverter(object, setter, iteratee, accumulator) { + baseForOwn(object, function(value, key, object) { + setter(accumulator, iteratee(value), key, object); + }); + return accumulator; + } + + /** + * The base implementation of `_.invoke` without support for individual + * method arguments. + * + * @private + * @param {Object} object The object to query. + * @param {Array|string} path The path of the method to invoke. + * @param {Array} args The arguments to invoke the method with. + * @returns {*} Returns the result of the invoked method. + */ + function baseInvoke(object, path, args) { + path = castPath(path, object); + object = parent(object, path); + var func = object == null ? object : object[toKey(last(path))]; + return func == null ? undefined : apply(func, object, args); + } + + /** + * The base implementation of `_.isArguments`. + * + * @private + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is an `arguments` object, + */ + function baseIsArguments(value) { + return isObjectLike(value) && baseGetTag(value) == argsTag; + } + + /** + * The base implementation of `_.isArrayBuffer` without Node.js optimizations. + * + * @private + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is an array buffer, else `false`. + */ + function baseIsArrayBuffer(value) { + return isObjectLike(value) && baseGetTag(value) == arrayBufferTag; + } + + /** + * The base implementation of `_.isDate` without Node.js optimizations. + * + * @private + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a date object, else `false`. + */ + function baseIsDate(value) { + return isObjectLike(value) && baseGetTag(value) == dateTag; + } + + /** + * The base implementation of `_.isEqual` which supports partial comparisons + * and tracks traversed objects. + * + * @private + * @param {*} value The value to compare. + * @param {*} other The other value to compare. + * @param {boolean} bitmask The bitmask flags. + * 1 - Unordered comparison + * 2 - Partial comparison + * @param {Function} [customizer] The function to customize comparisons. + * @param {Object} [stack] Tracks traversed `value` and `other` objects. + * @returns {boolean} Returns `true` if the values are equivalent, else `false`. + */ + function baseIsEqual(value, other, bitmask, customizer, stack) { + if (value === other) { + return true; + } + if (value == null || other == null || (!isObjectLike(value) && !isObjectLike(other))) { + return value !== value && other !== other; + } + return baseIsEqualDeep(value, other, bitmask, customizer, baseIsEqual, stack); + } + + /** + * A specialized version of `baseIsEqual` for arrays and objects which performs + * deep comparisons and tracks traversed objects enabling objects with circular + * references to be compared. + * + * @private + * @param {Object} object The object to compare. + * @param {Object} other The other object to compare. + * @param {number} bitmask The bitmask flags. See `baseIsEqual` for more details. + * @param {Function} customizer The function to customize comparisons. + * @param {Function} equalFunc The function to determine equivalents of values. + * @param {Object} [stack] Tracks traversed `object` and `other` objects. + * @returns {boolean} Returns `true` if the objects are equivalent, else `false`. + */ + function baseIsEqualDeep(object, other, bitmask, customizer, equalFunc, stack) { + var objIsArr = isArray(object), + othIsArr = isArray(other), + objTag = objIsArr ? arrayTag : getTag(object), + othTag = othIsArr ? arrayTag : getTag(other); + + objTag = objTag == argsTag ? objectTag : objTag; + othTag = othTag == argsTag ? objectTag : othTag; + + var objIsObj = objTag == objectTag, + othIsObj = othTag == objectTag, + isSameTag = objTag == othTag; + + if (isSameTag && isBuffer(object)) { + if (!isBuffer(other)) { + return false; + } + objIsArr = true; + objIsObj = false; + } + if (isSameTag && !objIsObj) { + stack || (stack = new Stack); + return (objIsArr || isTypedArray(object)) + ? equalArrays(object, other, bitmask, customizer, equalFunc, stack) + : equalByTag(object, other, objTag, bitmask, customizer, equalFunc, stack); + } + if (!(bitmask & COMPARE_PARTIAL_FLAG)) { + var objIsWrapped = objIsObj && hasOwnProperty.call(object, '__wrapped__'), + othIsWrapped = othIsObj && hasOwnProperty.call(other, '__wrapped__'); + + if (objIsWrapped || othIsWrapped) { + var objUnwrapped = objIsWrapped ? object.value() : object, + othUnwrapped = othIsWrapped ? other.value() : other; + + stack || (stack = new Stack); + return equalFunc(objUnwrapped, othUnwrapped, bitmask, customizer, stack); + } + } + if (!isSameTag) { + return false; + } + stack || (stack = new Stack); + return equalObjects(object, other, bitmask, customizer, equalFunc, stack); + } + + /** + * The base implementation of `_.isMap` without Node.js optimizations. + * + * @private + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a map, else `false`. + */ + function baseIsMap(value) { + return isObjectLike(value) && getTag(value) == mapTag; + } + + /** + * The base implementation of `_.isMatch` without support for iteratee shorthands. + * + * @private + * @param {Object} object The object to inspect. + * @param {Object} source The object of property values to match. + * @param {Array} matchData The property names, values, and compare flags to match. + * @param {Function} [customizer] The function to customize comparisons. + * @returns {boolean} Returns `true` if `object` is a match, else `false`. + */ + function baseIsMatch(object, source, matchData, customizer) { + var index = matchData.length, + length = index, + noCustomizer = !customizer; + + if (object == null) { + return !length; + } + object = Object(object); + while (index--) { + var data = matchData[index]; + if ((noCustomizer && data[2]) + ? data[1] !== object[data[0]] + : !(data[0] in object) + ) { + return false; + } + } + while (++index < length) { + data = matchData[index]; + var key = data[0], + objValue = object[key], + srcValue = data[1]; + + if (noCustomizer && data[2]) { + if (objValue === undefined && !(key in object)) { + return false; + } + } else { + var stack = new Stack; + if (customizer) { + var result = customizer(objValue, srcValue, key, object, source, stack); + } + if (!(result === undefined + ? baseIsEqual(srcValue, objValue, COMPARE_PARTIAL_FLAG | COMPARE_UNORDERED_FLAG, customizer, stack) + : result + )) { + return false; + } + } + } + return true; + } + + /** + * The base implementation of `_.isNative` without bad shim checks. + * + * @private + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a native function, + * else `false`. + */ + function baseIsNative(value) { + if (!isObject(value) || isMasked(value)) { + return false; + } + var pattern = isFunction(value) ? reIsNative : reIsHostCtor; + return pattern.test(toSource(value)); + } + + /** + * The base implementation of `_.isRegExp` without Node.js optimizations. + * + * @private + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a regexp, else `false`. + */ + function baseIsRegExp(value) { + return isObjectLike(value) && baseGetTag(value) == regexpTag; + } + + /** + * The base implementation of `_.isSet` without Node.js optimizations. + * + * @private + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a set, else `false`. + */ + function baseIsSet(value) { + return isObjectLike(value) && getTag(value) == setTag; + } + + /** + * The base implementation of `_.isTypedArray` without Node.js optimizations. + * + * @private + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a typed array, else `false`. + */ + function baseIsTypedArray(value) { + return isObjectLike(value) && + isLength(value.length) && !!typedArrayTags[baseGetTag(value)]; + } + + /** + * The base implementation of `_.iteratee`. + * + * @private + * @param {*} [value=_.identity] The value to convert to an iteratee. + * @returns {Function} Returns the iteratee. + */ + function baseIteratee(value) { + // Don't store the `typeof` result in a variable to avoid a JIT bug in Safari 9. + // See https://bugs.webkit.org/show_bug.cgi?id=156034 for more details. + if (typeof value == 'function') { + return value; + } + if (value == null) { + return identity; + } + if (typeof value == 'object') { + return isArray(value) + ? baseMatchesProperty(value[0], value[1]) + : baseMatches(value); + } + return property(value); + } + + /** + * The base implementation of `_.keys` which doesn't treat sparse arrays as dense. + * + * @private + * @param {Object} object The object to query. + * @returns {Array} Returns the array of property names. + */ + function baseKeys(object) { + if (!isPrototype(object)) { + return nativeKeys(object); + } + var result = []; + for (var key in Object(object)) { + if (hasOwnProperty.call(object, key) && key != 'constructor') { + result.push(key); + } + } + return result; + } + + /** + * The base implementation of `_.keysIn` which doesn't treat sparse arrays as dense. + * + * @private + * @param {Object} object The object to query. + * @returns {Array} Returns the array of property names. + */ + function baseKeysIn(object) { + if (!isObject(object)) { + return nativeKeysIn(object); + } + var isProto = isPrototype(object), + result = []; + + for (var key in object) { + if (!(key == 'constructor' && (isProto || !hasOwnProperty.call(object, key)))) { + result.push(key); + } + } + return result; + } + + /** + * The base implementation of `_.lt` which doesn't coerce arguments. + * + * @private + * @param {*} value The value to compare. + * @param {*} other The other value to compare. + * @returns {boolean} Returns `true` if `value` is less than `other`, + * else `false`. + */ + function baseLt(value, other) { + return value < other; + } + + /** + * The base implementation of `_.map` without support for iteratee shorthands. + * + * @private + * @param {Array|Object} collection The collection to iterate over. + * @param {Function} iteratee The function invoked per iteration. + * @returns {Array} Returns the new mapped array. + */ + function baseMap(collection, iteratee) { + var index = -1, + result = isArrayLike(collection) ? Array(collection.length) : []; + + baseEach(collection, function(value, key, collection) { + result[++index] = iteratee(value, key, collection); + }); + return result; + } + + /** + * The base implementation of `_.matches` which doesn't clone `source`. + * + * @private + * @param {Object} source The object of property values to match. + * @returns {Function} Returns the new spec function. + */ + function baseMatches(source) { + var matchData = getMatchData(source); + if (matchData.length == 1 && matchData[0][2]) { + return matchesStrictComparable(matchData[0][0], matchData[0][1]); + } + return function(object) { + return object === source || baseIsMatch(object, source, matchData); + }; + } + + /** + * The base implementation of `_.matchesProperty` which doesn't clone `srcValue`. + * + * @private + * @param {string} path The path of the property to get. + * @param {*} srcValue The value to match. + * @returns {Function} Returns the new spec function. + */ + function baseMatchesProperty(path, srcValue) { + if (isKey(path) && isStrictComparable(srcValue)) { + return matchesStrictComparable(toKey(path), srcValue); + } + return function(object) { + var objValue = get(object, path); + return (objValue === undefined && objValue === srcValue) + ? hasIn(object, path) + : baseIsEqual(srcValue, objValue, COMPARE_PARTIAL_FLAG | COMPARE_UNORDERED_FLAG); + }; + } + + /** + * The base implementation of `_.merge` without support for multiple sources. + * + * @private + * @param {Object} object The destination object. + * @param {Object} source The source object. + * @param {number} srcIndex The index of `source`. + * @param {Function} [customizer] The function to customize merged values. + * @param {Object} [stack] Tracks traversed source values and their merged + * counterparts. + */ + function baseMerge(object, source, srcIndex, customizer, stack) { + if (object === source) { + return; + } + baseFor(source, function(srcValue, key) { + if (isObject(srcValue)) { + stack || (stack = new Stack); + baseMergeDeep(object, source, key, srcIndex, baseMerge, customizer, stack); + } + else { + var newValue = customizer + ? customizer(object[key], srcValue, (key + ''), object, source, stack) + : undefined; + + if (newValue === undefined) { + newValue = srcValue; + } + assignMergeValue(object, key, newValue); + } + }, keysIn); + } + + /** + * A specialized version of `baseMerge` for arrays and objects which performs + * deep merges and tracks traversed objects enabling objects with circular + * references to be merged. + * + * @private + * @param {Object} object The destination object. + * @param {Object} source The source object. + * @param {string} key The key of the value to merge. + * @param {number} srcIndex The index of `source`. + * @param {Function} mergeFunc The function to merge values. + * @param {Function} [customizer] The function to customize assigned values. + * @param {Object} [stack] Tracks traversed source values and their merged + * counterparts. + */ + function baseMergeDeep(object, source, key, srcIndex, mergeFunc, customizer, stack) { + var objValue = object[key], + srcValue = source[key], + stacked = stack.get(srcValue); + + if (stacked) { + assignMergeValue(object, key, stacked); + return; + } + var newValue = customizer + ? customizer(objValue, srcValue, (key + ''), object, source, stack) + : undefined; + + var isCommon = newValue === undefined; + + if (isCommon) { + var isArr = isArray(srcValue), + isBuff = !isArr && isBuffer(srcValue), + isTyped = !isArr && !isBuff && isTypedArray(srcValue); + + newValue = srcValue; + if (isArr || isBuff || isTyped) { + if (isArray(objValue)) { + newValue = objValue; + } + else if (isArrayLikeObject(objValue)) { + newValue = copyArray(objValue); + } + else if (isBuff) { + isCommon = false; + newValue = cloneBuffer(srcValue, true); + } + else if (isTyped) { + isCommon = false; + newValue = cloneTypedArray(srcValue, true); + } + else { + newValue = []; + } + } + else if (isPlainObject(srcValue) || isArguments(srcValue)) { + newValue = objValue; + if (isArguments(objValue)) { + newValue = toPlainObject(objValue); + } + else if (!isObject(objValue) || (srcIndex && isFunction(objValue))) { + newValue = initCloneObject(srcValue); + } + } + else { + isCommon = false; + } + } + if (isCommon) { + // Recursively merge objects and arrays (susceptible to call stack limits). + stack.set(srcValue, newValue); + mergeFunc(newValue, srcValue, srcIndex, customizer, stack); + stack['delete'](srcValue); + } + assignMergeValue(object, key, newValue); + } + + /** + * The base implementation of `_.nth` which doesn't coerce arguments. + * + * @private + * @param {Array} array The array to query. + * @param {number} n The index of the element to return. + * @returns {*} Returns the nth element of `array`. + */ + function baseNth(array, n) { + var length = array.length; + if (!length) { + return; + } + n += n < 0 ? length : 0; + return isIndex(n, length) ? array[n] : undefined; + } + + /** + * The base implementation of `_.orderBy` without param guards. + * + * @private + * @param {Array|Object} collection The collection to iterate over. + * @param {Function[]|Object[]|string[]} iteratees The iteratees to sort by. + * @param {string[]} orders The sort orders of `iteratees`. + * @returns {Array} Returns the new sorted array. + */ + function baseOrderBy(collection, iteratees, orders) { + var index = -1; + iteratees = arrayMap(iteratees.length ? iteratees : [identity], baseUnary(getIteratee())); + + var result = baseMap(collection, function(value, key, collection) { + var criteria = arrayMap(iteratees, function(iteratee) { + return iteratee(value); + }); + return { 'criteria': criteria, 'index': ++index, 'value': value }; + }); + + return baseSortBy(result, function(object, other) { + return compareMultiple(object, other, orders); + }); + } + + /** + * The base implementation of `_.pick` without support for individual + * property identifiers. + * + * @private + * @param {Object} object The source object. + * @param {string[]} paths The property paths to pick. + * @returns {Object} Returns the new object. + */ + function basePick(object, paths) { + return basePickBy(object, paths, function(value, path) { + return hasIn(object, path); + }); + } + + /** + * The base implementation of `_.pickBy` without support for iteratee shorthands. + * + * @private + * @param {Object} object The source object. + * @param {string[]} paths The property paths to pick. + * @param {Function} predicate The function invoked per property. + * @returns {Object} Returns the new object. + */ + function basePickBy(object, paths, predicate) { + var index = -1, + length = paths.length, + result = {}; + + while (++index < length) { + var path = paths[index], + value = baseGet(object, path); + + if (predicate(value, path)) { + baseSet(result, castPath(path, object), value); + } + } + return result; + } + + /** + * A specialized version of `baseProperty` which supports deep paths. + * + * @private + * @param {Array|string} path The path of the property to get. + * @returns {Function} Returns the new accessor function. + */ + function basePropertyDeep(path) { + return function(object) { + return baseGet(object, path); + }; + } + + /** + * The base implementation of `_.pullAllBy` without support for iteratee + * shorthands. + * + * @private + * @param {Array} array The array to modify. + * @param {Array} values The values to remove. + * @param {Function} [iteratee] The iteratee invoked per element. + * @param {Function} [comparator] The comparator invoked per element. + * @returns {Array} Returns `array`. + */ + function basePullAll(array, values, iteratee, comparator) { + var indexOf = comparator ? baseIndexOfWith : baseIndexOf, + index = -1, + length = values.length, + seen = array; + + if (array === values) { + values = copyArray(values); + } + if (iteratee) { + seen = arrayMap(array, baseUnary(iteratee)); + } + while (++index < length) { + var fromIndex = 0, + value = values[index], + computed = iteratee ? iteratee(value) : value; + + while ((fromIndex = indexOf(seen, computed, fromIndex, comparator)) > -1) { + if (seen !== array) { + splice.call(seen, fromIndex, 1); + } + splice.call(array, fromIndex, 1); + } + } + return array; + } + + /** + * The base implementation of `_.pullAt` without support for individual + * indexes or capturing the removed elements. + * + * @private + * @param {Array} array The array to modify. + * @param {number[]} indexes The indexes of elements to remove. + * @returns {Array} Returns `array`. + */ + function basePullAt(array, indexes) { + var length = array ? indexes.length : 0, + lastIndex = length - 1; + + while (length--) { + var index = indexes[length]; + if (length == lastIndex || index !== previous) { + var previous = index; + if (isIndex(index)) { + splice.call(array, index, 1); + } else { + baseUnset(array, index); + } + } + } + return array; + } + + /** + * The base implementation of `_.random` without support for returning + * floating-point numbers. + * + * @private + * @param {number} lower The lower bound. + * @param {number} upper The upper bound. + * @returns {number} Returns the random number. + */ + function baseRandom(lower, upper) { + return lower + nativeFloor(nativeRandom() * (upper - lower + 1)); + } + + /** + * The base implementation of `_.range` and `_.rangeRight` which doesn't + * coerce arguments. + * + * @private + * @param {number} start The start of the range. + * @param {number} end The end of the range. + * @param {number} step The value to increment or decrement by. + * @param {boolean} [fromRight] Specify iterating from right to left. + * @returns {Array} Returns the range of numbers. + */ + function baseRange(start, end, step, fromRight) { + var index = -1, + length = nativeMax(nativeCeil((end - start) / (step || 1)), 0), + result = Array(length); + + while (length--) { + result[fromRight ? length : ++index] = start; + start += step; + } + return result; + } + + /** + * The base implementation of `_.repeat` which doesn't coerce arguments. + * + * @private + * @param {string} string The string to repeat. + * @param {number} n The number of times to repeat the string. + * @returns {string} Returns the repeated string. + */ + function baseRepeat(string, n) { + var result = ''; + if (!string || n < 1 || n > MAX_SAFE_INTEGER) { + return result; + } + // Leverage the exponentiation by squaring algorithm for a faster repeat. + // See https://en.wikipedia.org/wiki/Exponentiation_by_squaring for more details. + do { + if (n % 2) { + result += string; + } + n = nativeFloor(n / 2); + if (n) { + string += string; + } + } while (n); + + return result; + } + + /** + * The base implementation of `_.rest` which doesn't validate or coerce arguments. + * + * @private + * @param {Function} func The function to apply a rest parameter to. + * @param {number} [start=func.length-1] The start position of the rest parameter. + * @returns {Function} Returns the new function. + */ + function baseRest(func, start) { + return setToString(overRest(func, start, identity), func + ''); + } + + /** + * The base implementation of `_.sample`. + * + * @private + * @param {Array|Object} collection The collection to sample. + * @returns {*} Returns the random element. + */ + function baseSample(collection) { + return arraySample(values(collection)); + } + + /** + * The base implementation of `_.sampleSize` without param guards. + * + * @private + * @param {Array|Object} collection The collection to sample. + * @param {number} n The number of elements to sample. + * @returns {Array} Returns the random elements. + */ + function baseSampleSize(collection, n) { + var array = values(collection); + return shuffleSelf(array, baseClamp(n, 0, array.length)); + } + + /** + * The base implementation of `_.set`. + * + * @private + * @param {Object} object The object to modify. + * @param {Array|string} path The path of the property to set. + * @param {*} value The value to set. + * @param {Function} [customizer] The function to customize path creation. + * @returns {Object} Returns `object`. + */ + function baseSet(object, path, value, customizer) { + if (!isObject(object)) { + return object; + } + path = castPath(path, object); + + var index = -1, + length = path.length, + lastIndex = length - 1, + nested = object; + + while (nested != null && ++index < length) { + var key = toKey(path[index]), + newValue = value; + + if (index != lastIndex) { + var objValue = nested[key]; + newValue = customizer ? customizer(objValue, key, nested) : undefined; + if (newValue === undefined) { + newValue = isObject(objValue) + ? objValue + : (isIndex(path[index + 1]) ? [] : {}); + } + } + assignValue(nested, key, newValue); + nested = nested[key]; + } + return object; + } + + /** + * The base implementation of `setData` without support for hot loop shorting. + * + * @private + * @param {Function} func The function to associate metadata with. + * @param {*} data The metadata. + * @returns {Function} Returns `func`. + */ + var baseSetData = !metaMap ? identity : function(func, data) { + metaMap.set(func, data); + return func; + }; + + /** + * The base implementation of `setToString` without support for hot loop shorting. + * + * @private + * @param {Function} func The function to modify. + * @param {Function} string The `toString` result. + * @returns {Function} Returns `func`. + */ + var baseSetToString = !defineProperty ? identity : function(func, string) { + return defineProperty(func, 'toString', { + 'configurable': true, + 'enumerable': false, + 'value': constant(string), + 'writable': true + }); + }; + + /** + * The base implementation of `_.shuffle`. + * + * @private + * @param {Array|Object} collection The collection to shuffle. + * @returns {Array} Returns the new shuffled array. + */ + function baseShuffle(collection) { + return shuffleSelf(values(collection)); + } + + /** + * The base implementation of `_.slice` without an iteratee call guard. + * + * @private + * @param {Array} array The array to slice. + * @param {number} [start=0] The start position. + * @param {number} [end=array.length] The end position. + * @returns {Array} Returns the slice of `array`. + */ + function baseSlice(array, start, end) { + var index = -1, + length = array.length; + + if (start < 0) { + start = -start > length ? 0 : (length + start); + } + end = end > length ? length : end; + if (end < 0) { + end += length; + } + length = start > end ? 0 : ((end - start) >>> 0); + start >>>= 0; + + var result = Array(length); + while (++index < length) { + result[index] = array[index + start]; + } + return result; + } + + /** + * The base implementation of `_.some` without support for iteratee shorthands. + * + * @private + * @param {Array|Object} collection The collection to iterate over. + * @param {Function} predicate The function invoked per iteration. + * @returns {boolean} Returns `true` if any element passes the predicate check, + * else `false`. + */ + function baseSome(collection, predicate) { + var result; + + baseEach(collection, function(value, index, collection) { + result = predicate(value, index, collection); + return !result; + }); + return !!result; + } + + /** + * The base implementation of `_.sortedIndex` and `_.sortedLastIndex` which + * performs a binary search of `array` to determine the index at which `value` + * should be inserted into `array` in order to maintain its sort order. + * + * @private + * @param {Array} array The sorted array to inspect. + * @param {*} value The value to evaluate. + * @param {boolean} [retHighest] Specify returning the highest qualified index. + * @returns {number} Returns the index at which `value` should be inserted + * into `array`. + */ + function baseSortedIndex(array, value, retHighest) { + var low = 0, + high = array == null ? low : array.length; + + if (typeof value == 'number' && value === value && high <= HALF_MAX_ARRAY_LENGTH) { + while (low < high) { + var mid = (low + high) >>> 1, + computed = array[mid]; + + if (computed !== null && !isSymbol(computed) && + (retHighest ? (computed <= value) : (computed < value))) { + low = mid + 1; + } else { + high = mid; + } + } + return high; + } + return baseSortedIndexBy(array, value, identity, retHighest); + } + + /** + * The base implementation of `_.sortedIndexBy` and `_.sortedLastIndexBy` + * which invokes `iteratee` for `value` and each element of `array` to compute + * their sort ranking. The iteratee is invoked with one argument; (value). + * + * @private + * @param {Array} array The sorted array to inspect. + * @param {*} value The value to evaluate. + * @param {Function} iteratee The iteratee invoked per element. + * @param {boolean} [retHighest] Specify returning the highest qualified index. + * @returns {number} Returns the index at which `value` should be inserted + * into `array`. + */ + function baseSortedIndexBy(array, value, iteratee, retHighest) { + value = iteratee(value); + + var low = 0, + high = array == null ? 0 : array.length, + valIsNaN = value !== value, + valIsNull = value === null, + valIsSymbol = isSymbol(value), + valIsUndefined = value === undefined; + + while (low < high) { + var mid = nativeFloor((low + high) / 2), + computed = iteratee(array[mid]), + othIsDefined = computed !== undefined, + othIsNull = computed === null, + othIsReflexive = computed === computed, + othIsSymbol = isSymbol(computed); + + if (valIsNaN) { + var setLow = retHighest || othIsReflexive; + } else if (valIsUndefined) { + setLow = othIsReflexive && (retHighest || othIsDefined); + } else if (valIsNull) { + setLow = othIsReflexive && othIsDefined && (retHighest || !othIsNull); + } else if (valIsSymbol) { + setLow = othIsReflexive && othIsDefined && !othIsNull && (retHighest || !othIsSymbol); + } else if (othIsNull || othIsSymbol) { + setLow = false; + } else { + setLow = retHighest ? (computed <= value) : (computed < value); + } + if (setLow) { + low = mid + 1; + } else { + high = mid; + } + } + return nativeMin(high, MAX_ARRAY_INDEX); + } + + /** + * The base implementation of `_.sortedUniq` and `_.sortedUniqBy` without + * support for iteratee shorthands. + * + * @private + * @param {Array} array The array to inspect. + * @param {Function} [iteratee] The iteratee invoked per element. + * @returns {Array} Returns the new duplicate free array. + */ + function baseSortedUniq(array, iteratee) { + var index = -1, + length = array.length, + resIndex = 0, + result = []; + + while (++index < length) { + var value = array[index], + computed = iteratee ? iteratee(value) : value; + + if (!index || !eq(computed, seen)) { + var seen = computed; + result[resIndex++] = value === 0 ? 0 : value; + } + } + return result; + } + + /** + * The base implementation of `_.toNumber` which doesn't ensure correct + * conversions of binary, hexadecimal, or octal string values. + * + * @private + * @param {*} value The value to process. + * @returns {number} Returns the number. + */ + function baseToNumber(value) { + if (typeof value == 'number') { + return value; + } + if (isSymbol(value)) { + return NAN; + } + return +value; + } + + /** + * The base implementation of `_.toString` which doesn't convert nullish + * values to empty strings. + * + * @private + * @param {*} value The value to process. + * @returns {string} Returns the string. + */ + function baseToString(value) { + // Exit early for strings to avoid a performance hit in some environments. + if (typeof value == 'string') { + return value; + } + if (isArray(value)) { + // Recursively convert values (susceptible to call stack limits). + return arrayMap(value, baseToString) + ''; + } + if (isSymbol(value)) { + return symbolToString ? symbolToString.call(value) : ''; + } + var result = (value + ''); + return (result == '0' && (1 / value) == -INFINITY) ? '-0' : result; + } + + /** + * The base implementation of `_.uniqBy` without support for iteratee shorthands. + * + * @private + * @param {Array} array The array to inspect. + * @param {Function} [iteratee] The iteratee invoked per element. + * @param {Function} [comparator] The comparator invoked per element. + * @returns {Array} Returns the new duplicate free array. + */ + function baseUniq(array, iteratee, comparator) { + var index = -1, + includes = arrayIncludes, + length = array.length, + isCommon = true, + result = [], + seen = result; + + if (comparator) { + isCommon = false; + includes = arrayIncludesWith; + } + else if (length >= LARGE_ARRAY_SIZE) { + var set = iteratee ? null : createSet(array); + if (set) { + return setToArray(set); + } + isCommon = false; + includes = cacheHas; + seen = new SetCache; + } + else { + seen = iteratee ? [] : result; + } + outer: + while (++index < length) { + var value = array[index], + computed = iteratee ? iteratee(value) : value; + + value = (comparator || value !== 0) ? value : 0; + if (isCommon && computed === computed) { + var seenIndex = seen.length; + while (seenIndex--) { + if (seen[seenIndex] === computed) { + continue outer; + } + } + if (iteratee) { + seen.push(computed); + } + result.push(value); + } + else if (!includes(seen, computed, comparator)) { + if (seen !== result) { + seen.push(computed); + } + result.push(value); + } + } + return result; + } + + /** + * The base implementation of `_.unset`. + * + * @private + * @param {Object} object The object to modify. + * @param {Array|string} path The property path to unset. + * @returns {boolean} Returns `true` if the property is deleted, else `false`. + */ + function baseUnset(object, path) { + path = castPath(path, object); + object = parent(object, path); + return object == null || delete object[toKey(last(path))]; + } + + /** + * The base implementation of `_.update`. + * + * @private + * @param {Object} object The object to modify. + * @param {Array|string} path The path of the property to update. + * @param {Function} updater The function to produce the updated value. + * @param {Function} [customizer] The function to customize path creation. + * @returns {Object} Returns `object`. + */ + function baseUpdate(object, path, updater, customizer) { + return baseSet(object, path, updater(baseGet(object, path)), customizer); + } + + /** + * The base implementation of methods like `_.dropWhile` and `_.takeWhile` + * without support for iteratee shorthands. + * + * @private + * @param {Array} array The array to query. + * @param {Function} predicate The function invoked per iteration. + * @param {boolean} [isDrop] Specify dropping elements instead of taking them. + * @param {boolean} [fromRight] Specify iterating from right to left. + * @returns {Array} Returns the slice of `array`. + */ + function baseWhile(array, predicate, isDrop, fromRight) { + var length = array.length, + index = fromRight ? length : -1; + + while ((fromRight ? index-- : ++index < length) && + predicate(array[index], index, array)) {} + + return isDrop + ? baseSlice(array, (fromRight ? 0 : index), (fromRight ? index + 1 : length)) + : baseSlice(array, (fromRight ? index + 1 : 0), (fromRight ? length : index)); + } + + /** + * The base implementation of `wrapperValue` which returns the result of + * performing a sequence of actions on the unwrapped `value`, where each + * successive action is supplied the return value of the previous. + * + * @private + * @param {*} value The unwrapped value. + * @param {Array} actions Actions to perform to resolve the unwrapped value. + * @returns {*} Returns the resolved value. + */ + function baseWrapperValue(value, actions) { + var result = value; + if (result instanceof LazyWrapper) { + result = result.value(); + } + return arrayReduce(actions, function(result, action) { + return action.func.apply(action.thisArg, arrayPush([result], action.args)); + }, result); + } + + /** + * The base implementation of methods like `_.xor`, without support for + * iteratee shorthands, that accepts an array of arrays to inspect. + * + * @private + * @param {Array} arrays The arrays to inspect. + * @param {Function} [iteratee] The iteratee invoked per element. + * @param {Function} [comparator] The comparator invoked per element. + * @returns {Array} Returns the new array of values. + */ + function baseXor(arrays, iteratee, comparator) { + var length = arrays.length; + if (length < 2) { + return length ? baseUniq(arrays[0]) : []; + } + var index = -1, + result = Array(length); + + while (++index < length) { + var array = arrays[index], + othIndex = -1; + + while (++othIndex < length) { + if (othIndex != index) { + result[index] = baseDifference(result[index] || array, arrays[othIndex], iteratee, comparator); + } + } + } + return baseUniq(baseFlatten(result, 1), iteratee, comparator); + } + + /** + * This base implementation of `_.zipObject` which assigns values using `assignFunc`. + * + * @private + * @param {Array} props The property identifiers. + * @param {Array} values The property values. + * @param {Function} assignFunc The function to assign values. + * @returns {Object} Returns the new object. + */ + function baseZipObject(props, values, assignFunc) { + var index = -1, + length = props.length, + valsLength = values.length, + result = {}; + + while (++index < length) { + var value = index < valsLength ? values[index] : undefined; + assignFunc(result, props[index], value); + } + return result; + } + + /** + * Casts `value` to an empty array if it's not an array like object. + * + * @private + * @param {*} value The value to inspect. + * @returns {Array|Object} Returns the cast array-like object. + */ + function castArrayLikeObject(value) { + return isArrayLikeObject(value) ? value : []; + } + + /** + * Casts `value` to `identity` if it's not a function. + * + * @private + * @param {*} value The value to inspect. + * @returns {Function} Returns cast function. + */ + function castFunction(value) { + return typeof value == 'function' ? value : identity; + } + + /** + * Casts `value` to a path array if it's not one. + * + * @private + * @param {*} value The value to inspect. + * @param {Object} [object] The object to query keys on. + * @returns {Array} Returns the cast property path array. + */ + function castPath(value, object) { + if (isArray(value)) { + return value; + } + return isKey(value, object) ? [value] : stringToPath(toString(value)); + } + + /** + * A `baseRest` alias which can be replaced with `identity` by module + * replacement plugins. + * + * @private + * @type {Function} + * @param {Function} func The function to apply a rest parameter to. + * @returns {Function} Returns the new function. + */ + var castRest = baseRest; + + /** + * Casts `array` to a slice if it's needed. + * + * @private + * @param {Array} array The array to inspect. + * @param {number} start The start position. + * @param {number} [end=array.length] The end position. + * @returns {Array} Returns the cast slice. + */ + function castSlice(array, start, end) { + var length = array.length; + end = end === undefined ? length : end; + return (!start && end >= length) ? array : baseSlice(array, start, end); + } + + /** + * A simple wrapper around the global [`clearTimeout`](https://mdn.io/clearTimeout). + * + * @private + * @param {number|Object} id The timer id or timeout object of the timer to clear. + */ + var clearTimeout = ctxClearTimeout || function(id) { + return root.clearTimeout(id); + }; + + /** + * Creates a clone of `buffer`. + * + * @private + * @param {Buffer} buffer The buffer to clone. + * @param {boolean} [isDeep] Specify a deep clone. + * @returns {Buffer} Returns the cloned buffer. + */ + function cloneBuffer(buffer, isDeep) { + if (isDeep) { + return buffer.slice(); + } + var length = buffer.length, + result = allocUnsafe ? allocUnsafe(length) : new buffer.constructor(length); + + buffer.copy(result); + return result; + } + + /** + * Creates a clone of `arrayBuffer`. + * + * @private + * @param {ArrayBuffer} arrayBuffer The array buffer to clone. + * @returns {ArrayBuffer} Returns the cloned array buffer. + */ + function cloneArrayBuffer(arrayBuffer) { + var result = new arrayBuffer.constructor(arrayBuffer.byteLength); + new Uint8Array(result).set(new Uint8Array(arrayBuffer)); + return result; + } + + /** + * Creates a clone of `dataView`. + * + * @private + * @param {Object} dataView The data view to clone. + * @param {boolean} [isDeep] Specify a deep clone. + * @returns {Object} Returns the cloned data view. + */ + function cloneDataView(dataView, isDeep) { + var buffer = isDeep ? cloneArrayBuffer(dataView.buffer) : dataView.buffer; + return new dataView.constructor(buffer, dataView.byteOffset, dataView.byteLength); + } + + /** + * Creates a clone of `map`. + * + * @private + * @param {Object} map The map to clone. + * @param {Function} cloneFunc The function to clone values. + * @param {boolean} [isDeep] Specify a deep clone. + * @returns {Object} Returns the cloned map. + */ + function cloneMap(map, isDeep, cloneFunc) { + var array = isDeep ? cloneFunc(mapToArray(map), CLONE_DEEP_FLAG) : mapToArray(map); + return arrayReduce(array, addMapEntry, new map.constructor); + } + + /** + * Creates a clone of `regexp`. + * + * @private + * @param {Object} regexp The regexp to clone. + * @returns {Object} Returns the cloned regexp. + */ + function cloneRegExp(regexp) { + var result = new regexp.constructor(regexp.source, reFlags.exec(regexp)); + result.lastIndex = regexp.lastIndex; + return result; + } + + /** + * Creates a clone of `set`. + * + * @private + * @param {Object} set The set to clone. + * @param {Function} cloneFunc The function to clone values. + * @param {boolean} [isDeep] Specify a deep clone. + * @returns {Object} Returns the cloned set. + */ + function cloneSet(set, isDeep, cloneFunc) { + var array = isDeep ? cloneFunc(setToArray(set), CLONE_DEEP_FLAG) : setToArray(set); + return arrayReduce(array, addSetEntry, new set.constructor); + } + + /** + * Creates a clone of the `symbol` object. + * + * @private + * @param {Object} symbol The symbol object to clone. + * @returns {Object} Returns the cloned symbol object. + */ + function cloneSymbol(symbol) { + return symbolValueOf ? Object(symbolValueOf.call(symbol)) : {}; + } + + /** + * Creates a clone of `typedArray`. + * + * @private + * @param {Object} typedArray The typed array to clone. + * @param {boolean} [isDeep] Specify a deep clone. + * @returns {Object} Returns the cloned typed array. + */ + function cloneTypedArray(typedArray, isDeep) { + var buffer = isDeep ? cloneArrayBuffer(typedArray.buffer) : typedArray.buffer; + return new typedArray.constructor(buffer, typedArray.byteOffset, typedArray.length); + } + + /** + * Compares values to sort them in ascending order. + * + * @private + * @param {*} value The value to compare. + * @param {*} other The other value to compare. + * @returns {number} Returns the sort order indicator for `value`. + */ + function compareAscending(value, other) { + if (value !== other) { + var valIsDefined = value !== undefined, + valIsNull = value === null, + valIsReflexive = value === value, + valIsSymbol = isSymbol(value); + + var othIsDefined = other !== undefined, + othIsNull = other === null, + othIsReflexive = other === other, + othIsSymbol = isSymbol(other); + + if ((!othIsNull && !othIsSymbol && !valIsSymbol && value > other) || + (valIsSymbol && othIsDefined && othIsReflexive && !othIsNull && !othIsSymbol) || + (valIsNull && othIsDefined && othIsReflexive) || + (!valIsDefined && othIsReflexive) || + !valIsReflexive) { + return 1; + } + if ((!valIsNull && !valIsSymbol && !othIsSymbol && value < other) || + (othIsSymbol && valIsDefined && valIsReflexive && !valIsNull && !valIsSymbol) || + (othIsNull && valIsDefined && valIsReflexive) || + (!othIsDefined && valIsReflexive) || + !othIsReflexive) { + return -1; + } + } + return 0; + } + + /** + * Used by `_.orderBy` to compare multiple properties of a value to another + * and stable sort them. + * + * If `orders` is unspecified, all values are sorted in ascending order. Otherwise, + * specify an order of "desc" for descending or "asc" for ascending sort order + * of corresponding values. + * + * @private + * @param {Object} object The object to compare. + * @param {Object} other The other object to compare. + * @param {boolean[]|string[]} orders The order to sort by for each property. + * @returns {number} Returns the sort order indicator for `object`. + */ + function compareMultiple(object, other, orders) { + var index = -1, + objCriteria = object.criteria, + othCriteria = other.criteria, + length = objCriteria.length, + ordersLength = orders.length; + + while (++index < length) { + var result = compareAscending(objCriteria[index], othCriteria[index]); + if (result) { + if (index >= ordersLength) { + return result; + } + var order = orders[index]; + return result * (order == 'desc' ? -1 : 1); + } + } + // Fixes an `Array#sort` bug in the JS engine embedded in Adobe applications + // that causes it, under certain circumstances, to provide the same value for + // `object` and `other`. See https://github.com/jashkenas/underscore/pull/1247 + // for more details. + // + // This also ensures a stable sort in V8 and other engines. + // See https://bugs.chromium.org/p/v8/issues/detail?id=90 for more details. + return object.index - other.index; + } + + /** + * Creates an array that is the composition of partially applied arguments, + * placeholders, and provided arguments into a single array of arguments. + * + * @private + * @param {Array} args The provided arguments. + * @param {Array} partials The arguments to prepend to those provided. + * @param {Array} holders The `partials` placeholder indexes. + * @params {boolean} [isCurried] Specify composing for a curried function. + * @returns {Array} Returns the new array of composed arguments. + */ + function composeArgs(args, partials, holders, isCurried) { + var argsIndex = -1, + argsLength = args.length, + holdersLength = holders.length, + leftIndex = -1, + leftLength = partials.length, + rangeLength = nativeMax(argsLength - holdersLength, 0), + result = Array(leftLength + rangeLength), + isUncurried = !isCurried; + + while (++leftIndex < leftLength) { + result[leftIndex] = partials[leftIndex]; + } + while (++argsIndex < holdersLength) { + if (isUncurried || argsIndex < argsLength) { + result[holders[argsIndex]] = args[argsIndex]; + } + } + while (rangeLength--) { + result[leftIndex++] = args[argsIndex++]; + } + return result; + } + + /** + * This function is like `composeArgs` except that the arguments composition + * is tailored for `_.partialRight`. + * + * @private + * @param {Array} args The provided arguments. + * @param {Array} partials The arguments to append to those provided. + * @param {Array} holders The `partials` placeholder indexes. + * @params {boolean} [isCurried] Specify composing for a curried function. + * @returns {Array} Returns the new array of composed arguments. + */ + function composeArgsRight(args, partials, holders, isCurried) { + var argsIndex = -1, + argsLength = args.length, + holdersIndex = -1, + holdersLength = holders.length, + rightIndex = -1, + rightLength = partials.length, + rangeLength = nativeMax(argsLength - holdersLength, 0), + result = Array(rangeLength + rightLength), + isUncurried = !isCurried; + + while (++argsIndex < rangeLength) { + result[argsIndex] = args[argsIndex]; + } + var offset = argsIndex; + while (++rightIndex < rightLength) { + result[offset + rightIndex] = partials[rightIndex]; + } + while (++holdersIndex < holdersLength) { + if (isUncurried || argsIndex < argsLength) { + result[offset + holders[holdersIndex]] = args[argsIndex++]; + } + } + return result; + } + + /** + * Copies the values of `source` to `array`. + * + * @private + * @param {Array} source The array to copy values from. + * @param {Array} [array=[]] The array to copy values to. + * @returns {Array} Returns `array`. + */ + function copyArray(source, array) { + var index = -1, + length = source.length; + + array || (array = Array(length)); + while (++index < length) { + array[index] = source[index]; + } + return array; + } + + /** + * Copies properties of `source` to `object`. + * + * @private + * @param {Object} source The object to copy properties from. + * @param {Array} props The property identifiers to copy. + * @param {Object} [object={}] The object to copy properties to. + * @param {Function} [customizer] The function to customize copied values. + * @returns {Object} Returns `object`. + */ + function copyObject(source, props, object, customizer) { + var isNew = !object; + object || (object = {}); + + var index = -1, + length = props.length; + + while (++index < length) { + var key = props[index]; + + var newValue = customizer + ? customizer(object[key], source[key], key, object, source) + : undefined; + + if (newValue === undefined) { + newValue = source[key]; + } + if (isNew) { + baseAssignValue(object, key, newValue); + } else { + assignValue(object, key, newValue); + } + } + return object; + } + + /** + * Copies own symbols of `source` to `object`. + * + * @private + * @param {Object} source The object to copy symbols from. + * @param {Object} [object={}] The object to copy symbols to. + * @returns {Object} Returns `object`. + */ + function copySymbols(source, object) { + return copyObject(source, getSymbols(source), object); + } + + /** + * Copies own and inherited symbols of `source` to `object`. + * + * @private + * @param {Object} source The object to copy symbols from. + * @param {Object} [object={}] The object to copy symbols to. + * @returns {Object} Returns `object`. + */ + function copySymbolsIn(source, object) { + return copyObject(source, getSymbolsIn(source), object); + } + + /** + * Creates a function like `_.groupBy`. + * + * @private + * @param {Function} setter The function to set accumulator values. + * @param {Function} [initializer] The accumulator object initializer. + * @returns {Function} Returns the new aggregator function. + */ + function createAggregator(setter, initializer) { + return function(collection, iteratee) { + var func = isArray(collection) ? arrayAggregator : baseAggregator, + accumulator = initializer ? initializer() : {}; + + return func(collection, setter, getIteratee(iteratee, 2), accumulator); + }; + } + + /** + * Creates a function like `_.assign`. + * + * @private + * @param {Function} assigner The function to assign values. + * @returns {Function} Returns the new assigner function. + */ + function createAssigner(assigner) { + return baseRest(function(object, sources) { + var index = -1, + length = sources.length, + customizer = length > 1 ? sources[length - 1] : undefined, + guard = length > 2 ? sources[2] : undefined; + + customizer = (assigner.length > 3 && typeof customizer == 'function') + ? (length--, customizer) + : undefined; + + if (guard && isIterateeCall(sources[0], sources[1], guard)) { + customizer = length < 3 ? undefined : customizer; + length = 1; + } + object = Object(object); + while (++index < length) { + var source = sources[index]; + if (source) { + assigner(object, source, index, customizer); + } + } + return object; + }); + } + + /** + * Creates a `baseEach` or `baseEachRight` function. + * + * @private + * @param {Function} eachFunc The function to iterate over a collection. + * @param {boolean} [fromRight] Specify iterating from right to left. + * @returns {Function} Returns the new base function. + */ + function createBaseEach(eachFunc, fromRight) { + return function(collection, iteratee) { + if (collection == null) { + return collection; + } + if (!isArrayLike(collection)) { + return eachFunc(collection, iteratee); + } + var length = collection.length, + index = fromRight ? length : -1, + iterable = Object(collection); + + while ((fromRight ? index-- : ++index < length)) { + if (iteratee(iterable[index], index, iterable) === false) { + break; + } + } + return collection; + }; + } + + /** + * Creates a base function for methods like `_.forIn` and `_.forOwn`. + * + * @private + * @param {boolean} [fromRight] Specify iterating from right to left. + * @returns {Function} Returns the new base function. + */ + function createBaseFor(fromRight) { + return function(object, iteratee, keysFunc) { + var index = -1, + iterable = Object(object), + props = keysFunc(object), + length = props.length; + + while (length--) { + var key = props[fromRight ? length : ++index]; + if (iteratee(iterable[key], key, iterable) === false) { + break; + } + } + return object; + }; + } + + /** + * Creates a function that wraps `func` to invoke it with the optional `this` + * binding of `thisArg`. + * + * @private + * @param {Function} func The function to wrap. + * @param {number} bitmask The bitmask flags. See `createWrap` for more details. + * @param {*} [thisArg] The `this` binding of `func`. + * @returns {Function} Returns the new wrapped function. + */ + function createBind(func, bitmask, thisArg) { + var isBind = bitmask & WRAP_BIND_FLAG, + Ctor = createCtor(func); + + function wrapper() { + var fn = (this && this !== root && this instanceof wrapper) ? Ctor : func; + return fn.apply(isBind ? thisArg : this, arguments); + } + return wrapper; + } + + /** + * Creates a function like `_.lowerFirst`. + * + * @private + * @param {string} methodName The name of the `String` case method to use. + * @returns {Function} Returns the new case function. + */ + function createCaseFirst(methodName) { + return function(string) { + string = toString(string); + + var strSymbols = hasUnicode(string) + ? stringToArray(string) + : undefined; + + var chr = strSymbols + ? strSymbols[0] + : string.charAt(0); + + var trailing = strSymbols + ? castSlice(strSymbols, 1).join('') + : string.slice(1); + + return chr[methodName]() + trailing; + }; + } + + /** + * Creates a function like `_.camelCase`. + * + * @private + * @param {Function} callback The function to combine each word. + * @returns {Function} Returns the new compounder function. + */ + function createCompounder(callback) { + return function(string) { + return arrayReduce(words(deburr(string).replace(reApos, '')), callback, ''); + }; + } + + /** + * Creates a function that produces an instance of `Ctor` regardless of + * whether it was invoked as part of a `new` expression or by `call` or `apply`. + * + * @private + * @param {Function} Ctor The constructor to wrap. + * @returns {Function} Returns the new wrapped function. + */ + function createCtor(Ctor) { + return function() { + // Use a `switch` statement to work with class constructors. See + // http://ecma-international.org/ecma-262/7.0/#sec-ecmascript-function-objects-call-thisargument-argumentslist + // for more details. + var args = arguments; + switch (args.length) { + case 0: return new Ctor; + case 1: return new Ctor(args[0]); + case 2: return new Ctor(args[0], args[1]); + case 3: return new Ctor(args[0], args[1], args[2]); + case 4: return new Ctor(args[0], args[1], args[2], args[3]); + case 5: return new Ctor(args[0], args[1], args[2], args[3], args[4]); + case 6: return new Ctor(args[0], args[1], args[2], args[3], args[4], args[5]); + case 7: return new Ctor(args[0], args[1], args[2], args[3], args[4], args[5], args[6]); + } + var thisBinding = baseCreate(Ctor.prototype), + result = Ctor.apply(thisBinding, args); + + // Mimic the constructor's `return` behavior. + // See https://es5.github.io/#x13.2.2 for more details. + return isObject(result) ? result : thisBinding; + }; + } + + /** + * Creates a function that wraps `func` to enable currying. + * + * @private + * @param {Function} func The function to wrap. + * @param {number} bitmask The bitmask flags. See `createWrap` for more details. + * @param {number} arity The arity of `func`. + * @returns {Function} Returns the new wrapped function. + */ + function createCurry(func, bitmask, arity) { + var Ctor = createCtor(func); + + function wrapper() { + var length = arguments.length, + args = Array(length), + index = length, + placeholder = getHolder(wrapper); + + while (index--) { + args[index] = arguments[index]; + } + var holders = (length < 3 && args[0] !== placeholder && args[length - 1] !== placeholder) + ? [] + : replaceHolders(args, placeholder); + + length -= holders.length; + if (length < arity) { + return createRecurry( + func, bitmask, createHybrid, wrapper.placeholder, undefined, + args, holders, undefined, undefined, arity - length); + } + var fn = (this && this !== root && this instanceof wrapper) ? Ctor : func; + return apply(fn, this, args); + } + return wrapper; + } + + /** + * Creates a `_.find` or `_.findLast` function. + * + * @private + * @param {Function} findIndexFunc The function to find the collection index. + * @returns {Function} Returns the new find function. + */ + function createFind(findIndexFunc) { + return function(collection, predicate, fromIndex) { + var iterable = Object(collection); + if (!isArrayLike(collection)) { + var iteratee = getIteratee(predicate, 3); + collection = keys(collection); + predicate = function(key) { return iteratee(iterable[key], key, iterable); }; + } + var index = findIndexFunc(collection, predicate, fromIndex); + return index > -1 ? iterable[iteratee ? collection[index] : index] : undefined; + }; + } + + /** + * Creates a `_.flow` or `_.flowRight` function. + * + * @private + * @param {boolean} [fromRight] Specify iterating from right to left. + * @returns {Function} Returns the new flow function. + */ + function createFlow(fromRight) { + return flatRest(function(funcs) { + var length = funcs.length, + index = length, + prereq = LodashWrapper.prototype.thru; + + if (fromRight) { + funcs.reverse(); + } + while (index--) { + var func = funcs[index]; + if (typeof func != 'function') { + throw new TypeError(FUNC_ERROR_TEXT); + } + if (prereq && !wrapper && getFuncName(func) == 'wrapper') { + var wrapper = new LodashWrapper([], true); + } + } + index = wrapper ? index : length; + while (++index < length) { + func = funcs[index]; + + var funcName = getFuncName(func), + data = funcName == 'wrapper' ? getData(func) : undefined; + + if (data && isLaziable(data[0]) && + data[1] == (WRAP_ARY_FLAG | WRAP_CURRY_FLAG | WRAP_PARTIAL_FLAG | WRAP_REARG_FLAG) && + !data[4].length && data[9] == 1 + ) { + wrapper = wrapper[getFuncName(data[0])].apply(wrapper, data[3]); + } else { + wrapper = (func.length == 1 && isLaziable(func)) + ? wrapper[funcName]() + : wrapper.thru(func); + } + } + return function() { + var args = arguments, + value = args[0]; + + if (wrapper && args.length == 1 && isArray(value)) { + return wrapper.plant(value).value(); + } + var index = 0, + result = length ? funcs[index].apply(this, args) : value; + + while (++index < length) { + result = funcs[index].call(this, result); + } + return result; + }; + }); + } + + /** + * Creates a function that wraps `func` to invoke it with optional `this` + * binding of `thisArg`, partial application, and currying. + * + * @private + * @param {Function|string} func The function or method name to wrap. + * @param {number} bitmask The bitmask flags. See `createWrap` for more details. + * @param {*} [thisArg] The `this` binding of `func`. + * @param {Array} [partials] The arguments to prepend to those provided to + * the new function. + * @param {Array} [holders] The `partials` placeholder indexes. + * @param {Array} [partialsRight] The arguments to append to those provided + * to the new function. + * @param {Array} [holdersRight] The `partialsRight` placeholder indexes. + * @param {Array} [argPos] The argument positions of the new function. + * @param {number} [ary] The arity cap of `func`. + * @param {number} [arity] The arity of `func`. + * @returns {Function} Returns the new wrapped function. + */ + function createHybrid(func, bitmask, thisArg, partials, holders, partialsRight, holdersRight, argPos, ary, arity) { + var isAry = bitmask & WRAP_ARY_FLAG, + isBind = bitmask & WRAP_BIND_FLAG, + isBindKey = bitmask & WRAP_BIND_KEY_FLAG, + isCurried = bitmask & (WRAP_CURRY_FLAG | WRAP_CURRY_RIGHT_FLAG), + isFlip = bitmask & WRAP_FLIP_FLAG, + Ctor = isBindKey ? undefined : createCtor(func); + + function wrapper() { + var length = arguments.length, + args = Array(length), + index = length; + + while (index--) { + args[index] = arguments[index]; + } + if (isCurried) { + var placeholder = getHolder(wrapper), + holdersCount = countHolders(args, placeholder); + } + if (partials) { + args = composeArgs(args, partials, holders, isCurried); + } + if (partialsRight) { + args = composeArgsRight(args, partialsRight, holdersRight, isCurried); + } + length -= holdersCount; + if (isCurried && length < arity) { + var newHolders = replaceHolders(args, placeholder); + return createRecurry( + func, bitmask, createHybrid, wrapper.placeholder, thisArg, + args, newHolders, argPos, ary, arity - length + ); + } + var thisBinding = isBind ? thisArg : this, + fn = isBindKey ? thisBinding[func] : func; + + length = args.length; + if (argPos) { + args = reorder(args, argPos); + } else if (isFlip && length > 1) { + args.reverse(); + } + if (isAry && ary < length) { + args.length = ary; + } + if (this && this !== root && this instanceof wrapper) { + fn = Ctor || createCtor(fn); + } + return fn.apply(thisBinding, args); + } + return wrapper; + } + + /** + * Creates a function like `_.invertBy`. + * + * @private + * @param {Function} setter The function to set accumulator values. + * @param {Function} toIteratee The function to resolve iteratees. + * @returns {Function} Returns the new inverter function. + */ + function createInverter(setter, toIteratee) { + return function(object, iteratee) { + return baseInverter(object, setter, toIteratee(iteratee), {}); + }; + } + + /** + * Creates a function that performs a mathematical operation on two values. + * + * @private + * @param {Function} operator The function to perform the operation. + * @param {number} [defaultValue] The value used for `undefined` arguments. + * @returns {Function} Returns the new mathematical operation function. + */ + function createMathOperation(operator, defaultValue) { + return function(value, other) { + var result; + if (value === undefined && other === undefined) { + return defaultValue; + } + if (value !== undefined) { + result = value; + } + if (other !== undefined) { + if (result === undefined) { + return other; + } + if (typeof value == 'string' || typeof other == 'string') { + value = baseToString(value); + other = baseToString(other); + } else { + value = baseToNumber(value); + other = baseToNumber(other); + } + result = operator(value, other); + } + return result; + }; + } + + /** + * Creates a function like `_.over`. + * + * @private + * @param {Function} arrayFunc The function to iterate over iteratees. + * @returns {Function} Returns the new over function. + */ + function createOver(arrayFunc) { + return flatRest(function(iteratees) { + iteratees = arrayMap(iteratees, baseUnary(getIteratee())); + return baseRest(function(args) { + var thisArg = this; + return arrayFunc(iteratees, function(iteratee) { + return apply(iteratee, thisArg, args); + }); + }); + }); + } + + /** + * Creates the padding for `string` based on `length`. The `chars` string + * is truncated if the number of characters exceeds `length`. + * + * @private + * @param {number} length The padding length. + * @param {string} [chars=' '] The string used as padding. + * @returns {string} Returns the padding for `string`. + */ + function createPadding(length, chars) { + chars = chars === undefined ? ' ' : baseToString(chars); + + var charsLength = chars.length; + if (charsLength < 2) { + return charsLength ? baseRepeat(chars, length) : chars; + } + var result = baseRepeat(chars, nativeCeil(length / stringSize(chars))); + return hasUnicode(chars) + ? castSlice(stringToArray(result), 0, length).join('') + : result.slice(0, length); + } + + /** + * Creates a function that wraps `func` to invoke it with the `this` binding + * of `thisArg` and `partials` prepended to the arguments it receives. + * + * @private + * @param {Function} func The function to wrap. + * @param {number} bitmask The bitmask flags. See `createWrap` for more details. + * @param {*} thisArg The `this` binding of `func`. + * @param {Array} partials The arguments to prepend to those provided to + * the new function. + * @returns {Function} Returns the new wrapped function. + */ + function createPartial(func, bitmask, thisArg, partials) { + var isBind = bitmask & WRAP_BIND_FLAG, + Ctor = createCtor(func); + + function wrapper() { + var argsIndex = -1, + argsLength = arguments.length, + leftIndex = -1, + leftLength = partials.length, + args = Array(leftLength + argsLength), + fn = (this && this !== root && this instanceof wrapper) ? Ctor : func; + + while (++leftIndex < leftLength) { + args[leftIndex] = partials[leftIndex]; + } + while (argsLength--) { + args[leftIndex++] = arguments[++argsIndex]; + } + return apply(fn, isBind ? thisArg : this, args); + } + return wrapper; + } + + /** + * Creates a `_.range` or `_.rangeRight` function. + * + * @private + * @param {boolean} [fromRight] Specify iterating from right to left. + * @returns {Function} Returns the new range function. + */ + function createRange(fromRight) { + return function(start, end, step) { + if (step && typeof step != 'number' && isIterateeCall(start, end, step)) { + end = step = undefined; + } + // Ensure the sign of `-0` is preserved. + start = toFinite(start); + if (end === undefined) { + end = start; + start = 0; + } else { + end = toFinite(end); + } + step = step === undefined ? (start < end ? 1 : -1) : toFinite(step); + return baseRange(start, end, step, fromRight); + }; + } + + /** + * Creates a function that performs a relational operation on two values. + * + * @private + * @param {Function} operator The function to perform the operation. + * @returns {Function} Returns the new relational operation function. + */ + function createRelationalOperation(operator) { + return function(value, other) { + if (!(typeof value == 'string' && typeof other == 'string')) { + value = toNumber(value); + other = toNumber(other); + } + return operator(value, other); + }; + } + + /** + * Creates a function that wraps `func` to continue currying. + * + * @private + * @param {Function} func The function to wrap. + * @param {number} bitmask The bitmask flags. See `createWrap` for more details. + * @param {Function} wrapFunc The function to create the `func` wrapper. + * @param {*} placeholder The placeholder value. + * @param {*} [thisArg] The `this` binding of `func`. + * @param {Array} [partials] The arguments to prepend to those provided to + * the new function. + * @param {Array} [holders] The `partials` placeholder indexes. + * @param {Array} [argPos] The argument positions of the new function. + * @param {number} [ary] The arity cap of `func`. + * @param {number} [arity] The arity of `func`. + * @returns {Function} Returns the new wrapped function. + */ + function createRecurry(func, bitmask, wrapFunc, placeholder, thisArg, partials, holders, argPos, ary, arity) { + var isCurry = bitmask & WRAP_CURRY_FLAG, + newHolders = isCurry ? holders : undefined, + newHoldersRight = isCurry ? undefined : holders, + newPartials = isCurry ? partials : undefined, + newPartialsRight = isCurry ? undefined : partials; + + bitmask |= (isCurry ? WRAP_PARTIAL_FLAG : WRAP_PARTIAL_RIGHT_FLAG); + bitmask &= ~(isCurry ? WRAP_PARTIAL_RIGHT_FLAG : WRAP_PARTIAL_FLAG); + + if (!(bitmask & WRAP_CURRY_BOUND_FLAG)) { + bitmask &= ~(WRAP_BIND_FLAG | WRAP_BIND_KEY_FLAG); + } + var newData = [ + func, bitmask, thisArg, newPartials, newHolders, newPartialsRight, + newHoldersRight, argPos, ary, arity + ]; + + var result = wrapFunc.apply(undefined, newData); + if (isLaziable(func)) { + setData(result, newData); + } + result.placeholder = placeholder; + return setWrapToString(result, func, bitmask); + } + + /** + * Creates a function like `_.round`. + * + * @private + * @param {string} methodName The name of the `Math` method to use when rounding. + * @returns {Function} Returns the new round function. + */ + function createRound(methodName) { + var func = Math[methodName]; + return function(number, precision) { + number = toNumber(number); + precision = precision == null ? 0 : nativeMin(toInteger(precision), 292); + if (precision) { + // Shift with exponential notation to avoid floating-point issues. + // See [MDN](https://mdn.io/round#Examples) for more details. + var pair = (toString(number) + 'e').split('e'), + value = func(pair[0] + 'e' + (+pair[1] + precision)); + + pair = (toString(value) + 'e').split('e'); + return +(pair[0] + 'e' + (+pair[1] - precision)); + } + return func(number); + }; + } + + /** + * Creates a set object of `values`. + * + * @private + * @param {Array} values The values to add to the set. + * @returns {Object} Returns the new set. + */ + var createSet = !(Set && (1 / setToArray(new Set([,-0]))[1]) == INFINITY) ? noop : function(values) { + return new Set(values); + }; + + /** + * Creates a `_.toPairs` or `_.toPairsIn` function. + * + * @private + * @param {Function} keysFunc The function to get the keys of a given object. + * @returns {Function} Returns the new pairs function. + */ + function createToPairs(keysFunc) { + return function(object) { + var tag = getTag(object); + if (tag == mapTag) { + return mapToArray(object); + } + if (tag == setTag) { + return setToPairs(object); + } + return baseToPairs(object, keysFunc(object)); + }; + } + + /** + * Creates a function that either curries or invokes `func` with optional + * `this` binding and partially applied arguments. + * + * @private + * @param {Function|string} func The function or method name to wrap. + * @param {number} bitmask The bitmask flags. + * 1 - `_.bind` + * 2 - `_.bindKey` + * 4 - `_.curry` or `_.curryRight` of a bound function + * 8 - `_.curry` + * 16 - `_.curryRight` + * 32 - `_.partial` + * 64 - `_.partialRight` + * 128 - `_.rearg` + * 256 - `_.ary` + * 512 - `_.flip` + * @param {*} [thisArg] The `this` binding of `func`. + * @param {Array} [partials] The arguments to be partially applied. + * @param {Array} [holders] The `partials` placeholder indexes. + * @param {Array} [argPos] The argument positions of the new function. + * @param {number} [ary] The arity cap of `func`. + * @param {number} [arity] The arity of `func`. + * @returns {Function} Returns the new wrapped function. + */ + function createWrap(func, bitmask, thisArg, partials, holders, argPos, ary, arity) { + var isBindKey = bitmask & WRAP_BIND_KEY_FLAG; + if (!isBindKey && typeof func != 'function') { + throw new TypeError(FUNC_ERROR_TEXT); + } + var length = partials ? partials.length : 0; + if (!length) { + bitmask &= ~(WRAP_PARTIAL_FLAG | WRAP_PARTIAL_RIGHT_FLAG); + partials = holders = undefined; + } + ary = ary === undefined ? ary : nativeMax(toInteger(ary), 0); + arity = arity === undefined ? arity : toInteger(arity); + length -= holders ? holders.length : 0; + + if (bitmask & WRAP_PARTIAL_RIGHT_FLAG) { + var partialsRight = partials, + holdersRight = holders; + + partials = holders = undefined; + } + var data = isBindKey ? undefined : getData(func); + + var newData = [ + func, bitmask, thisArg, partials, holders, partialsRight, holdersRight, + argPos, ary, arity + ]; + + if (data) { + mergeData(newData, data); + } + func = newData[0]; + bitmask = newData[1]; + thisArg = newData[2]; + partials = newData[3]; + holders = newData[4]; + arity = newData[9] = newData[9] === undefined + ? (isBindKey ? 0 : func.length) + : nativeMax(newData[9] - length, 0); + + if (!arity && bitmask & (WRAP_CURRY_FLAG | WRAP_CURRY_RIGHT_FLAG)) { + bitmask &= ~(WRAP_CURRY_FLAG | WRAP_CURRY_RIGHT_FLAG); + } + if (!bitmask || bitmask == WRAP_BIND_FLAG) { + var result = createBind(func, bitmask, thisArg); + } else if (bitmask == WRAP_CURRY_FLAG || bitmask == WRAP_CURRY_RIGHT_FLAG) { + result = createCurry(func, bitmask, arity); + } else if ((bitmask == WRAP_PARTIAL_FLAG || bitmask == (WRAP_BIND_FLAG | WRAP_PARTIAL_FLAG)) && !holders.length) { + result = createPartial(func, bitmask, thisArg, partials); + } else { + result = createHybrid.apply(undefined, newData); + } + var setter = data ? baseSetData : setData; + return setWrapToString(setter(result, newData), func, bitmask); + } + + /** + * Used by `_.defaults` to customize its `_.assignIn` use to assign properties + * of source objects to the destination object for all destination properties + * that resolve to `undefined`. + * + * @private + * @param {*} objValue The destination value. + * @param {*} srcValue The source value. + * @param {string} key The key of the property to assign. + * @param {Object} object The parent object of `objValue`. + * @returns {*} Returns the value to assign. + */ + function customDefaultsAssignIn(objValue, srcValue, key, object) { + if (objValue === undefined || + (eq(objValue, objectProto[key]) && !hasOwnProperty.call(object, key))) { + return srcValue; + } + return objValue; + } + + /** + * Used by `_.defaultsDeep` to customize its `_.merge` use to merge source + * objects into destination objects that are passed thru. + * + * @private + * @param {*} objValue The destination value. + * @param {*} srcValue The source value. + * @param {string} key The key of the property to merge. + * @param {Object} object The parent object of `objValue`. + * @param {Object} source The parent object of `srcValue`. + * @param {Object} [stack] Tracks traversed source values and their merged + * counterparts. + * @returns {*} Returns the value to assign. + */ + function customDefaultsMerge(objValue, srcValue, key, object, source, stack) { + if (isObject(objValue) && isObject(srcValue)) { + // Recursively merge objects and arrays (susceptible to call stack limits). + stack.set(srcValue, objValue); + baseMerge(objValue, srcValue, undefined, customDefaultsMerge, stack); + stack['delete'](srcValue); + } + return objValue; + } + + /** + * Used by `_.omit` to customize its `_.cloneDeep` use to only clone plain + * objects. + * + * @private + * @param {*} value The value to inspect. + * @param {string} key The key of the property to inspect. + * @returns {*} Returns the uncloned value or `undefined` to defer cloning to `_.cloneDeep`. + */ + function customOmitClone(value) { + return isPlainObject(value) ? undefined : value; + } + + /** + * A specialized version of `baseIsEqualDeep` for arrays with support for + * partial deep comparisons. + * + * @private + * @param {Array} array The array to compare. + * @param {Array} other The other array to compare. + * @param {number} bitmask The bitmask flags. See `baseIsEqual` for more details. + * @param {Function} customizer The function to customize comparisons. + * @param {Function} equalFunc The function to determine equivalents of values. + * @param {Object} stack Tracks traversed `array` and `other` objects. + * @returns {boolean} Returns `true` if the arrays are equivalent, else `false`. + */ + function equalArrays(array, other, bitmask, customizer, equalFunc, stack) { + var isPartial = bitmask & COMPARE_PARTIAL_FLAG, + arrLength = array.length, + othLength = other.length; + + if (arrLength != othLength && !(isPartial && othLength > arrLength)) { + return false; + } + // Assume cyclic values are equal. + var stacked = stack.get(array); + if (stacked && stack.get(other)) { + return stacked == other; + } + var index = -1, + result = true, + seen = (bitmask & COMPARE_UNORDERED_FLAG) ? new SetCache : undefined; + + stack.set(array, other); + stack.set(other, array); + + // Ignore non-index properties. + while (++index < arrLength) { + var arrValue = array[index], + othValue = other[index]; + + if (customizer) { + var compared = isPartial + ? customizer(othValue, arrValue, index, other, array, stack) + : customizer(arrValue, othValue, index, array, other, stack); + } + if (compared !== undefined) { + if (compared) { + continue; + } + result = false; + break; + } + // Recursively compare arrays (susceptible to call stack limits). + if (seen) { + if (!arraySome(other, function(othValue, othIndex) { + if (!cacheHas(seen, othIndex) && + (arrValue === othValue || equalFunc(arrValue, othValue, bitmask, customizer, stack))) { + return seen.push(othIndex); + } + })) { + result = false; + break; + } + } else if (!( + arrValue === othValue || + equalFunc(arrValue, othValue, bitmask, customizer, stack) + )) { + result = false; + break; + } + } + stack['delete'](array); + stack['delete'](other); + return result; + } + + /** + * A specialized version of `baseIsEqualDeep` for comparing objects of + * the same `toStringTag`. + * + * **Note:** This function only supports comparing values with tags of + * `Boolean`, `Date`, `Error`, `Number`, `RegExp`, or `String`. + * + * @private + * @param {Object} object The object to compare. + * @param {Object} other The other object to compare. + * @param {string} tag The `toStringTag` of the objects to compare. + * @param {number} bitmask The bitmask flags. See `baseIsEqual` for more details. + * @param {Function} customizer The function to customize comparisons. + * @param {Function} equalFunc The function to determine equivalents of values. + * @param {Object} stack Tracks traversed `object` and `other` objects. + * @returns {boolean} Returns `true` if the objects are equivalent, else `false`. + */ + function equalByTag(object, other, tag, bitmask, customizer, equalFunc, stack) { + switch (tag) { + case dataViewTag: + if ((object.byteLength != other.byteLength) || + (object.byteOffset != other.byteOffset)) { + return false; + } + object = object.buffer; + other = other.buffer; + + case arrayBufferTag: + if ((object.byteLength != other.byteLength) || + !equalFunc(new Uint8Array(object), new Uint8Array(other))) { + return false; + } + return true; + + case boolTag: + case dateTag: + case numberTag: + // Coerce booleans to `1` or `0` and dates to milliseconds. + // Invalid dates are coerced to `NaN`. + return eq(+object, +other); + + case errorTag: + return object.name == other.name && object.message == other.message; + + case regexpTag: + case stringTag: + // Coerce regexes to strings and treat strings, primitives and objects, + // as equal. See http://www.ecma-international.org/ecma-262/7.0/#sec-regexp.prototype.tostring + // for more details. + return object == (other + ''); + + case mapTag: + var convert = mapToArray; + + case setTag: + var isPartial = bitmask & COMPARE_PARTIAL_FLAG; + convert || (convert = setToArray); + + if (object.size != other.size && !isPartial) { + return false; + } + // Assume cyclic values are equal. + var stacked = stack.get(object); + if (stacked) { + return stacked == other; + } + bitmask |= COMPARE_UNORDERED_FLAG; + + // Recursively compare objects (susceptible to call stack limits). + stack.set(object, other); + var result = equalArrays(convert(object), convert(other), bitmask, customizer, equalFunc, stack); + stack['delete'](object); + return result; + + case symbolTag: + if (symbolValueOf) { + return symbolValueOf.call(object) == symbolValueOf.call(other); + } + } + return false; + } + + /** + * A specialized version of `baseIsEqualDeep` for objects with support for + * partial deep comparisons. + * + * @private + * @param {Object} object The object to compare. + * @param {Object} other The other object to compare. + * @param {number} bitmask The bitmask flags. See `baseIsEqual` for more details. + * @param {Function} customizer The function to customize comparisons. + * @param {Function} equalFunc The function to determine equivalents of values. + * @param {Object} stack Tracks traversed `object` and `other` objects. + * @returns {boolean} Returns `true` if the objects are equivalent, else `false`. + */ + function equalObjects(object, other, bitmask, customizer, equalFunc, stack) { + var isPartial = bitmask & COMPARE_PARTIAL_FLAG, + objProps = getAllKeys(object), + objLength = objProps.length, + othProps = getAllKeys(other), + othLength = othProps.length; + + if (objLength != othLength && !isPartial) { + return false; + } + var index = objLength; + while (index--) { + var key = objProps[index]; + if (!(isPartial ? key in other : hasOwnProperty.call(other, key))) { + return false; + } + } + // Assume cyclic values are equal. + var stacked = stack.get(object); + if (stacked && stack.get(other)) { + return stacked == other; + } + var result = true; + stack.set(object, other); + stack.set(other, object); + + var skipCtor = isPartial; + while (++index < objLength) { + key = objProps[index]; + var objValue = object[key], + othValue = other[key]; + + if (customizer) { + var compared = isPartial + ? customizer(othValue, objValue, key, other, object, stack) + : customizer(objValue, othValue, key, object, other, stack); + } + // Recursively compare objects (susceptible to call stack limits). + if (!(compared === undefined + ? (objValue === othValue || equalFunc(objValue, othValue, bitmask, customizer, stack)) + : compared + )) { + result = false; + break; + } + skipCtor || (skipCtor = key == 'constructor'); + } + if (result && !skipCtor) { + var objCtor = object.constructor, + othCtor = other.constructor; + + // Non `Object` object instances with different constructors are not equal. + if (objCtor != othCtor && + ('constructor' in object && 'constructor' in other) && + !(typeof objCtor == 'function' && objCtor instanceof objCtor && + typeof othCtor == 'function' && othCtor instanceof othCtor)) { + result = false; + } + } + stack['delete'](object); + stack['delete'](other); + return result; + } + + /** + * A specialized version of `baseRest` which flattens the rest array. + * + * @private + * @param {Function} func The function to apply a rest parameter to. + * @returns {Function} Returns the new function. + */ + function flatRest(func) { + return setToString(overRest(func, undefined, flatten), func + ''); + } + + /** + * Creates an array of own enumerable property names and symbols of `object`. + * + * @private + * @param {Object} object The object to query. + * @returns {Array} Returns the array of property names and symbols. + */ + function getAllKeys(object) { + return baseGetAllKeys(object, keys, getSymbols); + } + + /** + * Creates an array of own and inherited enumerable property names and + * symbols of `object`. + * + * @private + * @param {Object} object The object to query. + * @returns {Array} Returns the array of property names and symbols. + */ + function getAllKeysIn(object) { + return baseGetAllKeys(object, keysIn, getSymbolsIn); + } + + /** + * Gets metadata for `func`. + * + * @private + * @param {Function} func The function to query. + * @returns {*} Returns the metadata for `func`. + */ + var getData = !metaMap ? noop : function(func) { + return metaMap.get(func); + }; + + /** + * Gets the name of `func`. + * + * @private + * @param {Function} func The function to query. + * @returns {string} Returns the function name. + */ + function getFuncName(func) { + var result = (func.name + ''), + array = realNames[result], + length = hasOwnProperty.call(realNames, result) ? array.length : 0; + + while (length--) { + var data = array[length], + otherFunc = data.func; + if (otherFunc == null || otherFunc == func) { + return data.name; + } + } + return result; + } + + /** + * Gets the argument placeholder value for `func`. + * + * @private + * @param {Function} func The function to inspect. + * @returns {*} Returns the placeholder value. + */ + function getHolder(func) { + var object = hasOwnProperty.call(lodash, 'placeholder') ? lodash : func; + return object.placeholder; + } + + /** + * Gets the appropriate "iteratee" function. If `_.iteratee` is customized, + * this function returns the custom method, otherwise it returns `baseIteratee`. + * If arguments are provided, the chosen function is invoked with them and + * its result is returned. + * + * @private + * @param {*} [value] The value to convert to an iteratee. + * @param {number} [arity] The arity of the created iteratee. + * @returns {Function} Returns the chosen function or its result. + */ + function getIteratee() { + var result = lodash.iteratee || iteratee; + result = result === iteratee ? baseIteratee : result; + return arguments.length ? result(arguments[0], arguments[1]) : result; + } + + /** + * Gets the data for `map`. + * + * @private + * @param {Object} map The map to query. + * @param {string} key The reference key. + * @returns {*} Returns the map data. + */ + function getMapData(map, key) { + var data = map.__data__; + return isKeyable(key) + ? data[typeof key == 'string' ? 'string' : 'hash'] + : data.map; + } + + /** + * Gets the property names, values, and compare flags of `object`. + * + * @private + * @param {Object} object The object to query. + * @returns {Array} Returns the match data of `object`. + */ + function getMatchData(object) { + var result = keys(object), + length = result.length; + + while (length--) { + var key = result[length], + value = object[key]; + + result[length] = [key, value, isStrictComparable(value)]; + } + return result; + } + + /** + * Gets the native function at `key` of `object`. + * + * @private + * @param {Object} object The object to query. + * @param {string} key The key of the method to get. + * @returns {*} Returns the function if it's native, else `undefined`. + */ + function getNative(object, key) { + var value = getValue(object, key); + return baseIsNative(value) ? value : undefined; + } + + /** + * A specialized version of `baseGetTag` which ignores `Symbol.toStringTag` values. + * + * @private + * @param {*} value The value to query. + * @returns {string} Returns the raw `toStringTag`. + */ + function getRawTag(value) { + var isOwn = hasOwnProperty.call(value, symToStringTag), + tag = value[symToStringTag]; + + try { + value[symToStringTag] = undefined; + var unmasked = true; + } catch (e) {} + + var result = nativeObjectToString.call(value); + if (unmasked) { + if (isOwn) { + value[symToStringTag] = tag; + } else { + delete value[symToStringTag]; + } + } + return result; + } + + /** + * Creates an array of the own enumerable symbols of `object`. + * + * @private + * @param {Object} object The object to query. + * @returns {Array} Returns the array of symbols. + */ + var getSymbols = !nativeGetSymbols ? stubArray : function(object) { + if (object == null) { + return []; + } + object = Object(object); + return arrayFilter(nativeGetSymbols(object), function(symbol) { + return propertyIsEnumerable.call(object, symbol); + }); + }; + + /** + * Creates an array of the own and inherited enumerable symbols of `object`. + * + * @private + * @param {Object} object The object to query. + * @returns {Array} Returns the array of symbols. + */ + var getSymbolsIn = !nativeGetSymbols ? stubArray : function(object) { + var result = []; + while (object) { + arrayPush(result, getSymbols(object)); + object = getPrototype(object); + } + return result; + }; + + /** + * Gets the `toStringTag` of `value`. + * + * @private + * @param {*} value The value to query. + * @returns {string} Returns the `toStringTag`. + */ + var getTag = baseGetTag; + + // Fallback for data views, maps, sets, and weak maps in IE 11 and promises in Node.js < 6. + if ((DataView && getTag(new DataView(new ArrayBuffer(1))) != dataViewTag) || + (Map && getTag(new Map) != mapTag) || + (Promise && getTag(Promise.resolve()) != promiseTag) || + (Set && getTag(new Set) != setTag) || + (WeakMap && getTag(new WeakMap) != weakMapTag)) { + getTag = function(value) { + var result = baseGetTag(value), + Ctor = result == objectTag ? value.constructor : undefined, + ctorString = Ctor ? toSource(Ctor) : ''; + + if (ctorString) { + switch (ctorString) { + case dataViewCtorString: return dataViewTag; + case mapCtorString: return mapTag; + case promiseCtorString: return promiseTag; + case setCtorString: return setTag; + case weakMapCtorString: return weakMapTag; + } + } + return result; + }; + } + + /** + * Gets the view, applying any `transforms` to the `start` and `end` positions. + * + * @private + * @param {number} start The start of the view. + * @param {number} end The end of the view. + * @param {Array} transforms The transformations to apply to the view. + * @returns {Object} Returns an object containing the `start` and `end` + * positions of the view. + */ + function getView(start, end, transforms) { + var index = -1, + length = transforms.length; + + while (++index < length) { + var data = transforms[index], + size = data.size; + + switch (data.type) { + case 'drop': start += size; break; + case 'dropRight': end -= size; break; + case 'take': end = nativeMin(end, start + size); break; + case 'takeRight': start = nativeMax(start, end - size); break; + } + } + return { 'start': start, 'end': end }; + } + + /** + * Extracts wrapper details from the `source` body comment. + * + * @private + * @param {string} source The source to inspect. + * @returns {Array} Returns the wrapper details. + */ + function getWrapDetails(source) { + var match = source.match(reWrapDetails); + return match ? match[1].split(reSplitDetails) : []; + } + + /** + * Checks if `path` exists on `object`. + * + * @private + * @param {Object} object The object to query. + * @param {Array|string} path The path to check. + * @param {Function} hasFunc The function to check properties. + * @returns {boolean} Returns `true` if `path` exists, else `false`. + */ + function hasPath(object, path, hasFunc) { + path = castPath(path, object); + + var index = -1, + length = path.length, + result = false; + + while (++index < length) { + var key = toKey(path[index]); + if (!(result = object != null && hasFunc(object, key))) { + break; + } + object = object[key]; + } + if (result || ++index != length) { + return result; + } + length = object == null ? 0 : object.length; + return !!length && isLength(length) && isIndex(key, length) && + (isArray(object) || isArguments(object)); + } + + /** + * Initializes an array clone. + * + * @private + * @param {Array} array The array to clone. + * @returns {Array} Returns the initialized clone. + */ + function initCloneArray(array) { + var length = array.length, + result = array.constructor(length); + + // Add properties assigned by `RegExp#exec`. + if (length && typeof array[0] == 'string' && hasOwnProperty.call(array, 'index')) { + result.index = array.index; + result.input = array.input; + } + return result; + } + + /** + * Initializes an object clone. + * + * @private + * @param {Object} object The object to clone. + * @returns {Object} Returns the initialized clone. + */ + function initCloneObject(object) { + return (typeof object.constructor == 'function' && !isPrototype(object)) + ? baseCreate(getPrototype(object)) + : {}; + } + + /** + * Initializes an object clone based on its `toStringTag`. + * + * **Note:** This function only supports cloning values with tags of + * `Boolean`, `Date`, `Error`, `Number`, `RegExp`, or `String`. + * + * @private + * @param {Object} object The object to clone. + * @param {string} tag The `toStringTag` of the object to clone. + * @param {Function} cloneFunc The function to clone values. + * @param {boolean} [isDeep] Specify a deep clone. + * @returns {Object} Returns the initialized clone. + */ + function initCloneByTag(object, tag, cloneFunc, isDeep) { + var Ctor = object.constructor; + switch (tag) { + case arrayBufferTag: + return cloneArrayBuffer(object); + + case boolTag: + case dateTag: + return new Ctor(+object); + + case dataViewTag: + return cloneDataView(object, isDeep); + + case float32Tag: case float64Tag: + case int8Tag: case int16Tag: case int32Tag: + case uint8Tag: case uint8ClampedTag: case uint16Tag: case uint32Tag: + return cloneTypedArray(object, isDeep); + + case mapTag: + return cloneMap(object, isDeep, cloneFunc); + + case numberTag: + case stringTag: + return new Ctor(object); + + case regexpTag: + return cloneRegExp(object); + + case setTag: + return cloneSet(object, isDeep, cloneFunc); + + case symbolTag: + return cloneSymbol(object); + } + } + + /** + * Inserts wrapper `details` in a comment at the top of the `source` body. + * + * @private + * @param {string} source The source to modify. + * @returns {Array} details The details to insert. + * @returns {string} Returns the modified source. + */ + function insertWrapDetails(source, details) { + var length = details.length; + if (!length) { + return source; + } + var lastIndex = length - 1; + details[lastIndex] = (length > 1 ? '& ' : '') + details[lastIndex]; + details = details.join(length > 2 ? ', ' : ' '); + return source.replace(reWrapComment, '{\n/* [wrapped with ' + details + '] */\n'); + } + + /** + * Checks if `value` is a flattenable `arguments` object or array. + * + * @private + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is flattenable, else `false`. + */ + function isFlattenable(value) { + return isArray(value) || isArguments(value) || + !!(spreadableSymbol && value && value[spreadableSymbol]); + } + + /** + * Checks if `value` is a valid array-like index. + * + * @private + * @param {*} value The value to check. + * @param {number} [length=MAX_SAFE_INTEGER] The upper bounds of a valid index. + * @returns {boolean} Returns `true` if `value` is a valid index, else `false`. + */ + function isIndex(value, length) { + length = length == null ? MAX_SAFE_INTEGER : length; + return !!length && + (typeof value == 'number' || reIsUint.test(value)) && + (value > -1 && value % 1 == 0 && value < length); + } + + /** + * Checks if the given arguments are from an iteratee call. + * + * @private + * @param {*} value The potential iteratee value argument. + * @param {*} index The potential iteratee index or key argument. + * @param {*} object The potential iteratee object argument. + * @returns {boolean} Returns `true` if the arguments are from an iteratee call, + * else `false`. + */ + function isIterateeCall(value, index, object) { + if (!isObject(object)) { + return false; + } + var type = typeof index; + if (type == 'number' + ? (isArrayLike(object) && isIndex(index, object.length)) + : (type == 'string' && index in object) + ) { + return eq(object[index], value); + } + return false; + } + + /** + * Checks if `value` is a property name and not a property path. + * + * @private + * @param {*} value The value to check. + * @param {Object} [object] The object to query keys on. + * @returns {boolean} Returns `true` if `value` is a property name, else `false`. + */ + function isKey(value, object) { + if (isArray(value)) { + return false; + } + var type = typeof value; + if (type == 'number' || type == 'symbol' || type == 'boolean' || + value == null || isSymbol(value)) { + return true; + } + return reIsPlainProp.test(value) || !reIsDeepProp.test(value) || + (object != null && value in Object(object)); + } + + /** + * Checks if `value` is suitable for use as unique object key. + * + * @private + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is suitable, else `false`. + */ + function isKeyable(value) { + var type = typeof value; + return (type == 'string' || type == 'number' || type == 'symbol' || type == 'boolean') + ? (value !== '__proto__') + : (value === null); + } + + /** + * Checks if `func` has a lazy counterpart. + * + * @private + * @param {Function} func The function to check. + * @returns {boolean} Returns `true` if `func` has a lazy counterpart, + * else `false`. + */ + function isLaziable(func) { + var funcName = getFuncName(func), + other = lodash[funcName]; + + if (typeof other != 'function' || !(funcName in LazyWrapper.prototype)) { + return false; + } + if (func === other) { + return true; + } + var data = getData(other); + return !!data && func === data[0]; + } + + /** + * Checks if `func` has its source masked. + * + * @private + * @param {Function} func The function to check. + * @returns {boolean} Returns `true` if `func` is masked, else `false`. + */ + function isMasked(func) { + return !!maskSrcKey && (maskSrcKey in func); + } + + /** + * Checks if `func` is capable of being masked. + * + * @private + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `func` is maskable, else `false`. + */ + var isMaskable = coreJsData ? isFunction : stubFalse; + + /** + * Checks if `value` is likely a prototype object. + * + * @private + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a prototype, else `false`. + */ + function isPrototype(value) { + var Ctor = value && value.constructor, + proto = (typeof Ctor == 'function' && Ctor.prototype) || objectProto; + + return value === proto; + } + + /** + * Checks if `value` is suitable for strict equality comparisons, i.e. `===`. + * + * @private + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` if suitable for strict + * equality comparisons, else `false`. + */ + function isStrictComparable(value) { + return value === value && !isObject(value); + } + + /** + * A specialized version of `matchesProperty` for source values suitable + * for strict equality comparisons, i.e. `===`. + * + * @private + * @param {string} key The key of the property to get. + * @param {*} srcValue The value to match. + * @returns {Function} Returns the new spec function. + */ + function matchesStrictComparable(key, srcValue) { + return function(object) { + if (object == null) { + return false; + } + return object[key] === srcValue && + (srcValue !== undefined || (key in Object(object))); + }; + } + + /** + * A specialized version of `_.memoize` which clears the memoized function's + * cache when it exceeds `MAX_MEMOIZE_SIZE`. + * + * @private + * @param {Function} func The function to have its output memoized. + * @returns {Function} Returns the new memoized function. + */ + function memoizeCapped(func) { + var result = memoize(func, function(key) { + if (cache.size === MAX_MEMOIZE_SIZE) { + cache.clear(); + } + return key; + }); + + var cache = result.cache; + return result; + } + + /** + * Merges the function metadata of `source` into `data`. + * + * Merging metadata reduces the number of wrappers used to invoke a function. + * This is possible because methods like `_.bind`, `_.curry`, and `_.partial` + * may be applied regardless of execution order. Methods like `_.ary` and + * `_.rearg` modify function arguments, making the order in which they are + * executed important, preventing the merging of metadata. However, we make + * an exception for a safe combined case where curried functions have `_.ary` + * and or `_.rearg` applied. + * + * @private + * @param {Array} data The destination metadata. + * @param {Array} source The source metadata. + * @returns {Array} Returns `data`. + */ + function mergeData(data, source) { + var bitmask = data[1], + srcBitmask = source[1], + newBitmask = bitmask | srcBitmask, + isCommon = newBitmask < (WRAP_BIND_FLAG | WRAP_BIND_KEY_FLAG | WRAP_ARY_FLAG); + + var isCombo = + ((srcBitmask == WRAP_ARY_FLAG) && (bitmask == WRAP_CURRY_FLAG)) || + ((srcBitmask == WRAP_ARY_FLAG) && (bitmask == WRAP_REARG_FLAG) && (data[7].length <= source[8])) || + ((srcBitmask == (WRAP_ARY_FLAG | WRAP_REARG_FLAG)) && (source[7].length <= source[8]) && (bitmask == WRAP_CURRY_FLAG)); + + // Exit early if metadata can't be merged. + if (!(isCommon || isCombo)) { + return data; + } + // Use source `thisArg` if available. + if (srcBitmask & WRAP_BIND_FLAG) { + data[2] = source[2]; + // Set when currying a bound function. + newBitmask |= bitmask & WRAP_BIND_FLAG ? 0 : WRAP_CURRY_BOUND_FLAG; + } + // Compose partial arguments. + var value = source[3]; + if (value) { + var partials = data[3]; + data[3] = partials ? composeArgs(partials, value, source[4]) : value; + data[4] = partials ? replaceHolders(data[3], PLACEHOLDER) : source[4]; + } + // Compose partial right arguments. + value = source[5]; + if (value) { + partials = data[5]; + data[5] = partials ? composeArgsRight(partials, value, source[6]) : value; + data[6] = partials ? replaceHolders(data[5], PLACEHOLDER) : source[6]; + } + // Use source `argPos` if available. + value = source[7]; + if (value) { + data[7] = value; + } + // Use source `ary` if it's smaller. + if (srcBitmask & WRAP_ARY_FLAG) { + data[8] = data[8] == null ? source[8] : nativeMin(data[8], source[8]); + } + // Use source `arity` if one is not provided. + if (data[9] == null) { + data[9] = source[9]; + } + // Use source `func` and merge bitmasks. + data[0] = source[0]; + data[1] = newBitmask; + + return data; + } + + /** + * This function is like + * [`Object.keys`](http://ecma-international.org/ecma-262/7.0/#sec-object.keys) + * except that it includes inherited enumerable properties. + * + * @private + * @param {Object} object The object to query. + * @returns {Array} Returns the array of property names. + */ + function nativeKeysIn(object) { + var result = []; + if (object != null) { + for (var key in Object(object)) { + result.push(key); + } + } + return result; + } + + /** + * Converts `value` to a string using `Object.prototype.toString`. + * + * @private + * @param {*} value The value to convert. + * @returns {string} Returns the converted string. + */ + function objectToString(value) { + return nativeObjectToString.call(value); + } + + /** + * A specialized version of `baseRest` which transforms the rest array. + * + * @private + * @param {Function} func The function to apply a rest parameter to. + * @param {number} [start=func.length-1] The start position of the rest parameter. + * @param {Function} transform The rest array transform. + * @returns {Function} Returns the new function. + */ + function overRest(func, start, transform) { + start = nativeMax(start === undefined ? (func.length - 1) : start, 0); + return function() { + var args = arguments, + index = -1, + length = nativeMax(args.length - start, 0), + array = Array(length); + + while (++index < length) { + array[index] = args[start + index]; + } + index = -1; + var otherArgs = Array(start + 1); + while (++index < start) { + otherArgs[index] = args[index]; + } + otherArgs[start] = transform(array); + return apply(func, this, otherArgs); + }; + } + + /** + * Gets the parent value at `path` of `object`. + * + * @private + * @param {Object} object The object to query. + * @param {Array} path The path to get the parent value of. + * @returns {*} Returns the parent value. + */ + function parent(object, path) { + return path.length < 2 ? object : baseGet(object, baseSlice(path, 0, -1)); + } + + /** + * Reorder `array` according to the specified indexes where the element at + * the first index is assigned as the first element, the element at + * the second index is assigned as the second element, and so on. + * + * @private + * @param {Array} array The array to reorder. + * @param {Array} indexes The arranged array indexes. + * @returns {Array} Returns `array`. + */ + function reorder(array, indexes) { + var arrLength = array.length, + length = nativeMin(indexes.length, arrLength), + oldArray = copyArray(array); + + while (length--) { + var index = indexes[length]; + array[length] = isIndex(index, arrLength) ? oldArray[index] : undefined; + } + return array; + } + + /** + * Sets metadata for `func`. + * + * **Note:** If this function becomes hot, i.e. is invoked a lot in a short + * period of time, it will trip its breaker and transition to an identity + * function to avoid garbage collection pauses in V8. See + * [V8 issue 2070](https://bugs.chromium.org/p/v8/issues/detail?id=2070) + * for more details. + * + * @private + * @param {Function} func The function to associate metadata with. + * @param {*} data The metadata. + * @returns {Function} Returns `func`. + */ + var setData = shortOut(baseSetData); + + /** + * A simple wrapper around the global [`setTimeout`](https://mdn.io/setTimeout). + * + * @private + * @param {Function} func The function to delay. + * @param {number} wait The number of milliseconds to delay invocation. + * @returns {number|Object} Returns the timer id or timeout object. + */ + var setTimeout = ctxSetTimeout || function(func, wait) { + return root.setTimeout(func, wait); + }; + + /** + * Sets the `toString` method of `func` to return `string`. + * + * @private + * @param {Function} func The function to modify. + * @param {Function} string The `toString` result. + * @returns {Function} Returns `func`. + */ + var setToString = shortOut(baseSetToString); + + /** + * Sets the `toString` method of `wrapper` to mimic the source of `reference` + * with wrapper details in a comment at the top of the source body. + * + * @private + * @param {Function} wrapper The function to modify. + * @param {Function} reference The reference function. + * @param {number} bitmask The bitmask flags. See `createWrap` for more details. + * @returns {Function} Returns `wrapper`. + */ + function setWrapToString(wrapper, reference, bitmask) { + var source = (reference + ''); + return setToString(wrapper, insertWrapDetails(source, updateWrapDetails(getWrapDetails(source), bitmask))); + } + + /** + * Creates a function that'll short out and invoke `identity` instead + * of `func` when it's called `HOT_COUNT` or more times in `HOT_SPAN` + * milliseconds. + * + * @private + * @param {Function} func The function to restrict. + * @returns {Function} Returns the new shortable function. + */ + function shortOut(func) { + var count = 0, + lastCalled = 0; + + return function() { + var stamp = nativeNow(), + remaining = HOT_SPAN - (stamp - lastCalled); + + lastCalled = stamp; + if (remaining > 0) { + if (++count >= HOT_COUNT) { + return arguments[0]; + } + } else { + count = 0; + } + return func.apply(undefined, arguments); + }; + } + + /** + * A specialized version of `_.shuffle` which mutates and sets the size of `array`. + * + * @private + * @param {Array} array The array to shuffle. + * @param {number} [size=array.length] The size of `array`. + * @returns {Array} Returns `array`. + */ + function shuffleSelf(array, size) { + var index = -1, + length = array.length, + lastIndex = length - 1; + + size = size === undefined ? length : size; + while (++index < size) { + var rand = baseRandom(index, lastIndex), + value = array[rand]; + + array[rand] = array[index]; + array[index] = value; + } + array.length = size; + return array; + } + + /** + * Converts `string` to a property path array. + * + * @private + * @param {string} string The string to convert. + * @returns {Array} Returns the property path array. + */ + var stringToPath = memoizeCapped(function(string) { + var result = []; + if (reLeadingDot.test(string)) { + result.push(''); + } + string.replace(rePropName, function(match, number, quote, string) { + result.push(quote ? string.replace(reEscapeChar, '$1') : (number || match)); + }); + return result; + }); + + /** + * Converts `value` to a string key if it's not a string or symbol. + * + * @private + * @param {*} value The value to inspect. + * @returns {string|symbol} Returns the key. + */ + function toKey(value) { + if (typeof value == 'string' || isSymbol(value)) { + return value; + } + var result = (value + ''); + return (result == '0' && (1 / value) == -INFINITY) ? '-0' : result; + } + + /** + * Converts `func` to its source code. + * + * @private + * @param {Function} func The function to convert. + * @returns {string} Returns the source code. + */ + function toSource(func) { + if (func != null) { + try { + return funcToString.call(func); + } catch (e) {} + try { + return (func + ''); + } catch (e) {} + } + return ''; + } + + /** + * Updates wrapper `details` based on `bitmask` flags. + * + * @private + * @returns {Array} details The details to modify. + * @param {number} bitmask The bitmask flags. See `createWrap` for more details. + * @returns {Array} Returns `details`. + */ + function updateWrapDetails(details, bitmask) { + arrayEach(wrapFlags, function(pair) { + var value = '_.' + pair[0]; + if ((bitmask & pair[1]) && !arrayIncludes(details, value)) { + details.push(value); + } + }); + return details.sort(); + } + + /** + * Creates a clone of `wrapper`. + * + * @private + * @param {Object} wrapper The wrapper to clone. + * @returns {Object} Returns the cloned wrapper. + */ + function wrapperClone(wrapper) { + if (wrapper instanceof LazyWrapper) { + return wrapper.clone(); + } + var result = new LodashWrapper(wrapper.__wrapped__, wrapper.__chain__); + result.__actions__ = copyArray(wrapper.__actions__); + result.__index__ = wrapper.__index__; + result.__values__ = wrapper.__values__; + return result; + } + + /*------------------------------------------------------------------------*/ + + /** + * Creates an array of elements split into groups the length of `size`. + * If `array` can't be split evenly, the final chunk will be the remaining + * elements. + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category Array + * @param {Array} array The array to process. + * @param {number} [size=1] The length of each chunk + * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`. + * @returns {Array} Returns the new array of chunks. + * @example + * + * _.chunk(['a', 'b', 'c', 'd'], 2); + * // => [['a', 'b'], ['c', 'd']] + * + * _.chunk(['a', 'b', 'c', 'd'], 3); + * // => [['a', 'b', 'c'], ['d']] + */ + function chunk(array, size, guard) { + if ((guard ? isIterateeCall(array, size, guard) : size === undefined)) { + size = 1; + } else { + size = nativeMax(toInteger(size), 0); + } + var length = array == null ? 0 : array.length; + if (!length || size < 1) { + return []; + } + var index = 0, + resIndex = 0, + result = Array(nativeCeil(length / size)); + + while (index < length) { + result[resIndex++] = baseSlice(array, index, (index += size)); + } + return result; + } + + /** + * Creates an array with all falsey values removed. The values `false`, `null`, + * `0`, `""`, `undefined`, and `NaN` are falsey. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Array + * @param {Array} array The array to compact. + * @returns {Array} Returns the new array of filtered values. + * @example + * + * _.compact([0, 1, false, 2, '', 3]); + * // => [1, 2, 3] + */ + function compact(array) { + var index = -1, + length = array == null ? 0 : array.length, + resIndex = 0, + result = []; + + while (++index < length) { + var value = array[index]; + if (value) { + result[resIndex++] = value; + } + } + return result; + } + + /** + * Creates a new array concatenating `array` with any additional arrays + * and/or values. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Array + * @param {Array} array The array to concatenate. + * @param {...*} [values] The values to concatenate. + * @returns {Array} Returns the new concatenated array. + * @example + * + * var array = [1]; + * var other = _.concat(array, 2, [3], [[4]]); + * + * console.log(other); + * // => [1, 2, 3, [4]] + * + * console.log(array); + * // => [1] + */ + function concat() { + var length = arguments.length; + if (!length) { + return []; + } + var args = Array(length - 1), + array = arguments[0], + index = length; + + while (index--) { + args[index - 1] = arguments[index]; + } + return arrayPush(isArray(array) ? copyArray(array) : [array], baseFlatten(args, 1)); + } + + /** + * Creates an array of `array` values not included in the other given arrays + * using [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero) + * for equality comparisons. The order and references of result values are + * determined by the first array. + * + * **Note:** Unlike `_.pullAll`, this method returns a new array. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Array + * @param {Array} array The array to inspect. + * @param {...Array} [values] The values to exclude. + * @returns {Array} Returns the new array of filtered values. + * @see _.without, _.xor + * @example + * + * _.difference([2, 1], [2, 3]); + * // => [1] + */ + var difference = baseRest(function(array, values) { + return isArrayLikeObject(array) + ? baseDifference(array, baseFlatten(values, 1, isArrayLikeObject, true)) + : []; + }); + + /** + * This method is like `_.difference` except that it accepts `iteratee` which + * is invoked for each element of `array` and `values` to generate the criterion + * by which they're compared. The order and references of result values are + * determined by the first array. The iteratee is invoked with one argument: + * (value). + * + * **Note:** Unlike `_.pullAllBy`, this method returns a new array. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Array + * @param {Array} array The array to inspect. + * @param {...Array} [values] The values to exclude. + * @param {Function} [iteratee=_.identity] The iteratee invoked per element. + * @returns {Array} Returns the new array of filtered values. + * @example + * + * _.differenceBy([2.1, 1.2], [2.3, 3.4], Math.floor); + * // => [1.2] + * + * // The `_.property` iteratee shorthand. + * _.differenceBy([{ 'x': 2 }, { 'x': 1 }], [{ 'x': 1 }], 'x'); + * // => [{ 'x': 2 }] + */ + var differenceBy = baseRest(function(array, values) { + var iteratee = last(values); + if (isArrayLikeObject(iteratee)) { + iteratee = undefined; + } + return isArrayLikeObject(array) + ? baseDifference(array, baseFlatten(values, 1, isArrayLikeObject, true), getIteratee(iteratee, 2)) + : []; + }); + + /** + * This method is like `_.difference` except that it accepts `comparator` + * which is invoked to compare elements of `array` to `values`. The order and + * references of result values are determined by the first array. The comparator + * is invoked with two arguments: (arrVal, othVal). + * + * **Note:** Unlike `_.pullAllWith`, this method returns a new array. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Array + * @param {Array} array The array to inspect. + * @param {...Array} [values] The values to exclude. + * @param {Function} [comparator] The comparator invoked per element. + * @returns {Array} Returns the new array of filtered values. + * @example + * + * var objects = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }]; + * + * _.differenceWith(objects, [{ 'x': 1, 'y': 2 }], _.isEqual); + * // => [{ 'x': 2, 'y': 1 }] + */ + var differenceWith = baseRest(function(array, values) { + var comparator = last(values); + if (isArrayLikeObject(comparator)) { + comparator = undefined; + } + return isArrayLikeObject(array) + ? baseDifference(array, baseFlatten(values, 1, isArrayLikeObject, true), undefined, comparator) + : []; + }); + + /** + * Creates a slice of `array` with `n` elements dropped from the beginning. + * + * @static + * @memberOf _ + * @since 0.5.0 + * @category Array + * @param {Array} array The array to query. + * @param {number} [n=1] The number of elements to drop. + * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`. + * @returns {Array} Returns the slice of `array`. + * @example + * + * _.drop([1, 2, 3]); + * // => [2, 3] + * + * _.drop([1, 2, 3], 2); + * // => [3] + * + * _.drop([1, 2, 3], 5); + * // => [] + * + * _.drop([1, 2, 3], 0); + * // => [1, 2, 3] + */ + function drop(array, n, guard) { + var length = array == null ? 0 : array.length; + if (!length) { + return []; + } + n = (guard || n === undefined) ? 1 : toInteger(n); + return baseSlice(array, n < 0 ? 0 : n, length); + } + + /** + * Creates a slice of `array` with `n` elements dropped from the end. + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category Array + * @param {Array} array The array to query. + * @param {number} [n=1] The number of elements to drop. + * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`. + * @returns {Array} Returns the slice of `array`. + * @example + * + * _.dropRight([1, 2, 3]); + * // => [1, 2] + * + * _.dropRight([1, 2, 3], 2); + * // => [1] + * + * _.dropRight([1, 2, 3], 5); + * // => [] + * + * _.dropRight([1, 2, 3], 0); + * // => [1, 2, 3] + */ + function dropRight(array, n, guard) { + var length = array == null ? 0 : array.length; + if (!length) { + return []; + } + n = (guard || n === undefined) ? 1 : toInteger(n); + n = length - n; + return baseSlice(array, 0, n < 0 ? 0 : n); + } + + /** + * Creates a slice of `array` excluding elements dropped from the end. + * Elements are dropped until `predicate` returns falsey. The predicate is + * invoked with three arguments: (value, index, array). + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category Array + * @param {Array} array The array to query. + * @param {Function} [predicate=_.identity] The function invoked per iteration. + * @returns {Array} Returns the slice of `array`. + * @example + * + * var users = [ + * { 'user': 'barney', 'active': true }, + * { 'user': 'fred', 'active': false }, + * { 'user': 'pebbles', 'active': false } + * ]; + * + * _.dropRightWhile(users, function(o) { return !o.active; }); + * // => objects for ['barney'] + * + * // The `_.matches` iteratee shorthand. + * _.dropRightWhile(users, { 'user': 'pebbles', 'active': false }); + * // => objects for ['barney', 'fred'] + * + * // The `_.matchesProperty` iteratee shorthand. + * _.dropRightWhile(users, ['active', false]); + * // => objects for ['barney'] + * + * // The `_.property` iteratee shorthand. + * _.dropRightWhile(users, 'active'); + * // => objects for ['barney', 'fred', 'pebbles'] + */ + function dropRightWhile(array, predicate) { + return (array && array.length) + ? baseWhile(array, getIteratee(predicate, 3), true, true) + : []; + } + + /** + * Creates a slice of `array` excluding elements dropped from the beginning. + * Elements are dropped until `predicate` returns falsey. The predicate is + * invoked with three arguments: (value, index, array). + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category Array + * @param {Array} array The array to query. + * @param {Function} [predicate=_.identity] The function invoked per iteration. + * @returns {Array} Returns the slice of `array`. + * @example + * + * var users = [ + * { 'user': 'barney', 'active': false }, + * { 'user': 'fred', 'active': false }, + * { 'user': 'pebbles', 'active': true } + * ]; + * + * _.dropWhile(users, function(o) { return !o.active; }); + * // => objects for ['pebbles'] + * + * // The `_.matches` iteratee shorthand. + * _.dropWhile(users, { 'user': 'barney', 'active': false }); + * // => objects for ['fred', 'pebbles'] + * + * // The `_.matchesProperty` iteratee shorthand. + * _.dropWhile(users, ['active', false]); + * // => objects for ['pebbles'] + * + * // The `_.property` iteratee shorthand. + * _.dropWhile(users, 'active'); + * // => objects for ['barney', 'fred', 'pebbles'] + */ + function dropWhile(array, predicate) { + return (array && array.length) + ? baseWhile(array, getIteratee(predicate, 3), true) + : []; + } + + /** + * Fills elements of `array` with `value` from `start` up to, but not + * including, `end`. + * + * **Note:** This method mutates `array`. + * + * @static + * @memberOf _ + * @since 3.2.0 + * @category Array + * @param {Array} array The array to fill. + * @param {*} value The value to fill `array` with. + * @param {number} [start=0] The start position. + * @param {number} [end=array.length] The end position. + * @returns {Array} Returns `array`. + * @example + * + * var array = [1, 2, 3]; + * + * _.fill(array, 'a'); + * console.log(array); + * // => ['a', 'a', 'a'] + * + * _.fill(Array(3), 2); + * // => [2, 2, 2] + * + * _.fill([4, 6, 8, 10], '*', 1, 3); + * // => [4, '*', '*', 10] + */ + function fill(array, value, start, end) { + var length = array == null ? 0 : array.length; + if (!length) { + return []; + } + if (start && typeof start != 'number' && isIterateeCall(array, value, start)) { + start = 0; + end = length; + } + return baseFill(array, value, start, end); + } + + /** + * This method is like `_.find` except that it returns the index of the first + * element `predicate` returns truthy for instead of the element itself. + * + * @static + * @memberOf _ + * @since 1.1.0 + * @category Array + * @param {Array} array The array to inspect. + * @param {Function} [predicate=_.identity] The function invoked per iteration. + * @param {number} [fromIndex=0] The index to search from. + * @returns {number} Returns the index of the found element, else `-1`. + * @example + * + * var users = [ + * { 'user': 'barney', 'active': false }, + * { 'user': 'fred', 'active': false }, + * { 'user': 'pebbles', 'active': true } + * ]; + * + * _.findIndex(users, function(o) { return o.user == 'barney'; }); + * // => 0 + * + * // The `_.matches` iteratee shorthand. + * _.findIndex(users, { 'user': 'fred', 'active': false }); + * // => 1 + * + * // The `_.matchesProperty` iteratee shorthand. + * _.findIndex(users, ['active', false]); + * // => 0 + * + * // The `_.property` iteratee shorthand. + * _.findIndex(users, 'active'); + * // => 2 + */ + function findIndex(array, predicate, fromIndex) { + var length = array == null ? 0 : array.length; + if (!length) { + return -1; + } + var index = fromIndex == null ? 0 : toInteger(fromIndex); + if (index < 0) { + index = nativeMax(length + index, 0); + } + return baseFindIndex(array, getIteratee(predicate, 3), index); + } + + /** + * This method is like `_.findIndex` except that it iterates over elements + * of `collection` from right to left. + * + * @static + * @memberOf _ + * @since 2.0.0 + * @category Array + * @param {Array} array The array to inspect. + * @param {Function} [predicate=_.identity] The function invoked per iteration. + * @param {number} [fromIndex=array.length-1] The index to search from. + * @returns {number} Returns the index of the found element, else `-1`. + * @example + * + * var users = [ + * { 'user': 'barney', 'active': true }, + * { 'user': 'fred', 'active': false }, + * { 'user': 'pebbles', 'active': false } + * ]; + * + * _.findLastIndex(users, function(o) { return o.user == 'pebbles'; }); + * // => 2 + * + * // The `_.matches` iteratee shorthand. + * _.findLastIndex(users, { 'user': 'barney', 'active': true }); + * // => 0 + * + * // The `_.matchesProperty` iteratee shorthand. + * _.findLastIndex(users, ['active', false]); + * // => 2 + * + * // The `_.property` iteratee shorthand. + * _.findLastIndex(users, 'active'); + * // => 0 + */ + function findLastIndex(array, predicate, fromIndex) { + var length = array == null ? 0 : array.length; + if (!length) { + return -1; + } + var index = length - 1; + if (fromIndex !== undefined) { + index = toInteger(fromIndex); + index = fromIndex < 0 + ? nativeMax(length + index, 0) + : nativeMin(index, length - 1); + } + return baseFindIndex(array, getIteratee(predicate, 3), index, true); + } + + /** + * Flattens `array` a single level deep. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Array + * @param {Array} array The array to flatten. + * @returns {Array} Returns the new flattened array. + * @example + * + * _.flatten([1, [2, [3, [4]], 5]]); + * // => [1, 2, [3, [4]], 5] + */ + function flatten(array) { + var length = array == null ? 0 : array.length; + return length ? baseFlatten(array, 1) : []; + } + + /** + * Recursively flattens `array`. + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category Array + * @param {Array} array The array to flatten. + * @returns {Array} Returns the new flattened array. + * @example + * + * _.flattenDeep([1, [2, [3, [4]], 5]]); + * // => [1, 2, 3, 4, 5] + */ + function flattenDeep(array) { + var length = array == null ? 0 : array.length; + return length ? baseFlatten(array, INFINITY) : []; + } + + /** + * Recursively flatten `array` up to `depth` times. + * + * @static + * @memberOf _ + * @since 4.4.0 + * @category Array + * @param {Array} array The array to flatten. + * @param {number} [depth=1] The maximum recursion depth. + * @returns {Array} Returns the new flattened array. + * @example + * + * var array = [1, [2, [3, [4]], 5]]; + * + * _.flattenDepth(array, 1); + * // => [1, 2, [3, [4]], 5] + * + * _.flattenDepth(array, 2); + * // => [1, 2, 3, [4], 5] + */ + function flattenDepth(array, depth) { + var length = array == null ? 0 : array.length; + if (!length) { + return []; + } + depth = depth === undefined ? 1 : toInteger(depth); + return baseFlatten(array, depth); + } + + /** + * The inverse of `_.toPairs`; this method returns an object composed + * from key-value `pairs`. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Array + * @param {Array} pairs The key-value pairs. + * @returns {Object} Returns the new object. + * @example + * + * _.fromPairs([['a', 1], ['b', 2]]); + * // => { 'a': 1, 'b': 2 } + */ + function fromPairs(pairs) { + var index = -1, + length = pairs == null ? 0 : pairs.length, + result = {}; + + while (++index < length) { + var pair = pairs[index]; + result[pair[0]] = pair[1]; + } + return result; + } + + /** + * Gets the first element of `array`. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @alias first + * @category Array + * @param {Array} array The array to query. + * @returns {*} Returns the first element of `array`. + * @example + * + * _.head([1, 2, 3]); + * // => 1 + * + * _.head([]); + * // => undefined + */ + function head(array) { + return (array && array.length) ? array[0] : undefined; + } + + /** + * Gets the index at which the first occurrence of `value` is found in `array` + * using [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero) + * for equality comparisons. If `fromIndex` is negative, it's used as the + * offset from the end of `array`. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Array + * @param {Array} array The array to inspect. + * @param {*} value The value to search for. + * @param {number} [fromIndex=0] The index to search from. + * @returns {number} Returns the index of the matched value, else `-1`. + * @example + * + * _.indexOf([1, 2, 1, 2], 2); + * // => 1 + * + * // Search from the `fromIndex`. + * _.indexOf([1, 2, 1, 2], 2, 2); + * // => 3 + */ + function indexOf(array, value, fromIndex) { + var length = array == null ? 0 : array.length; + if (!length) { + return -1; + } + var index = fromIndex == null ? 0 : toInteger(fromIndex); + if (index < 0) { + index = nativeMax(length + index, 0); + } + return baseIndexOf(array, value, index); + } + + /** + * Gets all but the last element of `array`. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Array + * @param {Array} array The array to query. + * @returns {Array} Returns the slice of `array`. + * @example + * + * _.initial([1, 2, 3]); + * // => [1, 2] + */ + function initial(array) { + var length = array == null ? 0 : array.length; + return length ? baseSlice(array, 0, -1) : []; + } + + /** + * Creates an array of unique values that are included in all given arrays + * using [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero) + * for equality comparisons. The order and references of result values are + * determined by the first array. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Array + * @param {...Array} [arrays] The arrays to inspect. + * @returns {Array} Returns the new array of intersecting values. + * @example + * + * _.intersection([2, 1], [2, 3]); + * // => [2] + */ + var intersection = baseRest(function(arrays) { + var mapped = arrayMap(arrays, castArrayLikeObject); + return (mapped.length && mapped[0] === arrays[0]) + ? baseIntersection(mapped) + : []; + }); + + /** + * This method is like `_.intersection` except that it accepts `iteratee` + * which is invoked for each element of each `arrays` to generate the criterion + * by which they're compared. The order and references of result values are + * determined by the first array. The iteratee is invoked with one argument: + * (value). + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Array + * @param {...Array} [arrays] The arrays to inspect. + * @param {Function} [iteratee=_.identity] The iteratee invoked per element. + * @returns {Array} Returns the new array of intersecting values. + * @example + * + * _.intersectionBy([2.1, 1.2], [2.3, 3.4], Math.floor); + * // => [2.1] + * + * // The `_.property` iteratee shorthand. + * _.intersectionBy([{ 'x': 1 }], [{ 'x': 2 }, { 'x': 1 }], 'x'); + * // => [{ 'x': 1 }] + */ + var intersectionBy = baseRest(function(arrays) { + var iteratee = last(arrays), + mapped = arrayMap(arrays, castArrayLikeObject); + + if (iteratee === last(mapped)) { + iteratee = undefined; + } else { + mapped.pop(); + } + return (mapped.length && mapped[0] === arrays[0]) + ? baseIntersection(mapped, getIteratee(iteratee, 2)) + : []; + }); + + /** + * This method is like `_.intersection` except that it accepts `comparator` + * which is invoked to compare elements of `arrays`. The order and references + * of result values are determined by the first array. The comparator is + * invoked with two arguments: (arrVal, othVal). + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Array + * @param {...Array} [arrays] The arrays to inspect. + * @param {Function} [comparator] The comparator invoked per element. + * @returns {Array} Returns the new array of intersecting values. + * @example + * + * var objects = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }]; + * var others = [{ 'x': 1, 'y': 1 }, { 'x': 1, 'y': 2 }]; + * + * _.intersectionWith(objects, others, _.isEqual); + * // => [{ 'x': 1, 'y': 2 }] + */ + var intersectionWith = baseRest(function(arrays) { + var comparator = last(arrays), + mapped = arrayMap(arrays, castArrayLikeObject); + + comparator = typeof comparator == 'function' ? comparator : undefined; + if (comparator) { + mapped.pop(); + } + return (mapped.length && mapped[0] === arrays[0]) + ? baseIntersection(mapped, undefined, comparator) + : []; + }); + + /** + * Converts all elements in `array` into a string separated by `separator`. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Array + * @param {Array} array The array to convert. + * @param {string} [separator=','] The element separator. + * @returns {string} Returns the joined string. + * @example + * + * _.join(['a', 'b', 'c'], '~'); + * // => 'a~b~c' + */ + function join(array, separator) { + return array == null ? '' : nativeJoin.call(array, separator); + } + + /** + * Gets the last element of `array`. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Array + * @param {Array} array The array to query. + * @returns {*} Returns the last element of `array`. + * @example + * + * _.last([1, 2, 3]); + * // => 3 + */ + function last(array) { + var length = array == null ? 0 : array.length; + return length ? array[length - 1] : undefined; + } + + /** + * This method is like `_.indexOf` except that it iterates over elements of + * `array` from right to left. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Array + * @param {Array} array The array to inspect. + * @param {*} value The value to search for. + * @param {number} [fromIndex=array.length-1] The index to search from. + * @returns {number} Returns the index of the matched value, else `-1`. + * @example + * + * _.lastIndexOf([1, 2, 1, 2], 2); + * // => 3 + * + * // Search from the `fromIndex`. + * _.lastIndexOf([1, 2, 1, 2], 2, 2); + * // => 1 + */ + function lastIndexOf(array, value, fromIndex) { + var length = array == null ? 0 : array.length; + if (!length) { + return -1; + } + var index = length; + if (fromIndex !== undefined) { + index = toInteger(fromIndex); + index = index < 0 ? nativeMax(length + index, 0) : nativeMin(index, length - 1); + } + return value === value + ? strictLastIndexOf(array, value, index) + : baseFindIndex(array, baseIsNaN, index, true); + } + + /** + * Gets the element at index `n` of `array`. If `n` is negative, the nth + * element from the end is returned. + * + * @static + * @memberOf _ + * @since 4.11.0 + * @category Array + * @param {Array} array The array to query. + * @param {number} [n=0] The index of the element to return. + * @returns {*} Returns the nth element of `array`. + * @example + * + * var array = ['a', 'b', 'c', 'd']; + * + * _.nth(array, 1); + * // => 'b' + * + * _.nth(array, -2); + * // => 'c'; + */ + function nth(array, n) { + return (array && array.length) ? baseNth(array, toInteger(n)) : undefined; + } + + /** + * Removes all given values from `array` using + * [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero) + * for equality comparisons. + * + * **Note:** Unlike `_.without`, this method mutates `array`. Use `_.remove` + * to remove elements from an array by predicate. + * + * @static + * @memberOf _ + * @since 2.0.0 + * @category Array + * @param {Array} array The array to modify. + * @param {...*} [values] The values to remove. + * @returns {Array} Returns `array`. + * @example + * + * var array = ['a', 'b', 'c', 'a', 'b', 'c']; + * + * _.pull(array, 'a', 'c'); + * console.log(array); + * // => ['b', 'b'] + */ + var pull = baseRest(pullAll); + + /** + * This method is like `_.pull` except that it accepts an array of values to remove. + * + * **Note:** Unlike `_.difference`, this method mutates `array`. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Array + * @param {Array} array The array to modify. + * @param {Array} values The values to remove. + * @returns {Array} Returns `array`. + * @example + * + * var array = ['a', 'b', 'c', 'a', 'b', 'c']; + * + * _.pullAll(array, ['a', 'c']); + * console.log(array); + * // => ['b', 'b'] + */ + function pullAll(array, values) { + return (array && array.length && values && values.length) + ? basePullAll(array, values) + : array; + } + + /** + * This method is like `_.pullAll` except that it accepts `iteratee` which is + * invoked for each element of `array` and `values` to generate the criterion + * by which they're compared. The iteratee is invoked with one argument: (value). + * + * **Note:** Unlike `_.differenceBy`, this method mutates `array`. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Array + * @param {Array} array The array to modify. + * @param {Array} values The values to remove. + * @param {Function} [iteratee=_.identity] The iteratee invoked per element. + * @returns {Array} Returns `array`. + * @example + * + * var array = [{ 'x': 1 }, { 'x': 2 }, { 'x': 3 }, { 'x': 1 }]; + * + * _.pullAllBy(array, [{ 'x': 1 }, { 'x': 3 }], 'x'); + * console.log(array); + * // => [{ 'x': 2 }] + */ + function pullAllBy(array, values, iteratee) { + return (array && array.length && values && values.length) + ? basePullAll(array, values, getIteratee(iteratee, 2)) + : array; + } + + /** + * This method is like `_.pullAll` except that it accepts `comparator` which + * is invoked to compare elements of `array` to `values`. The comparator is + * invoked with two arguments: (arrVal, othVal). + * + * **Note:** Unlike `_.differenceWith`, this method mutates `array`. + * + * @static + * @memberOf _ + * @since 4.6.0 + * @category Array + * @param {Array} array The array to modify. + * @param {Array} values The values to remove. + * @param {Function} [comparator] The comparator invoked per element. + * @returns {Array} Returns `array`. + * @example + * + * var array = [{ 'x': 1, 'y': 2 }, { 'x': 3, 'y': 4 }, { 'x': 5, 'y': 6 }]; + * + * _.pullAllWith(array, [{ 'x': 3, 'y': 4 }], _.isEqual); + * console.log(array); + * // => [{ 'x': 1, 'y': 2 }, { 'x': 5, 'y': 6 }] + */ + function pullAllWith(array, values, comparator) { + return (array && array.length && values && values.length) + ? basePullAll(array, values, undefined, comparator) + : array; + } + + /** + * Removes elements from `array` corresponding to `indexes` and returns an + * array of removed elements. + * + * **Note:** Unlike `_.at`, this method mutates `array`. + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category Array + * @param {Array} array The array to modify. + * @param {...(number|number[])} [indexes] The indexes of elements to remove. + * @returns {Array} Returns the new array of removed elements. + * @example + * + * var array = ['a', 'b', 'c', 'd']; + * var pulled = _.pullAt(array, [1, 3]); + * + * console.log(array); + * // => ['a', 'c'] + * + * console.log(pulled); + * // => ['b', 'd'] + */ + var pullAt = flatRest(function(array, indexes) { + var length = array == null ? 0 : array.length, + result = baseAt(array, indexes); + + basePullAt(array, arrayMap(indexes, function(index) { + return isIndex(index, length) ? +index : index; + }).sort(compareAscending)); + + return result; + }); + + /** + * Removes all elements from `array` that `predicate` returns truthy for + * and returns an array of the removed elements. The predicate is invoked + * with three arguments: (value, index, array). + * + * **Note:** Unlike `_.filter`, this method mutates `array`. Use `_.pull` + * to pull elements from an array by value. + * + * @static + * @memberOf _ + * @since 2.0.0 + * @category Array + * @param {Array} array The array to modify. + * @param {Function} [predicate=_.identity] The function invoked per iteration. + * @returns {Array} Returns the new array of removed elements. + * @example + * + * var array = [1, 2, 3, 4]; + * var evens = _.remove(array, function(n) { + * return n % 2 == 0; + * }); + * + * console.log(array); + * // => [1, 3] + * + * console.log(evens); + * // => [2, 4] + */ + function remove(array, predicate) { + var result = []; + if (!(array && array.length)) { + return result; + } + var index = -1, + indexes = [], + length = array.length; + + predicate = getIteratee(predicate, 3); + while (++index < length) { + var value = array[index]; + if (predicate(value, index, array)) { + result.push(value); + indexes.push(index); + } + } + basePullAt(array, indexes); + return result; + } + + /** + * Reverses `array` so that the first element becomes the last, the second + * element becomes the second to last, and so on. + * + * **Note:** This method mutates `array` and is based on + * [`Array#reverse`](https://mdn.io/Array/reverse). + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Array + * @param {Array} array The array to modify. + * @returns {Array} Returns `array`. + * @example + * + * var array = [1, 2, 3]; + * + * _.reverse(array); + * // => [3, 2, 1] + * + * console.log(array); + * // => [3, 2, 1] + */ + function reverse(array) { + return array == null ? array : nativeReverse.call(array); + } + + /** + * Creates a slice of `array` from `start` up to, but not including, `end`. + * + * **Note:** This method is used instead of + * [`Array#slice`](https://mdn.io/Array/slice) to ensure dense arrays are + * returned. + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category Array + * @param {Array} array The array to slice. + * @param {number} [start=0] The start position. + * @param {number} [end=array.length] The end position. + * @returns {Array} Returns the slice of `array`. + */ + function slice(array, start, end) { + var length = array == null ? 0 : array.length; + if (!length) { + return []; + } + if (end && typeof end != 'number' && isIterateeCall(array, start, end)) { + start = 0; + end = length; + } + else { + start = start == null ? 0 : toInteger(start); + end = end === undefined ? length : toInteger(end); + } + return baseSlice(array, start, end); + } + + /** + * Uses a binary search to determine the lowest index at which `value` + * should be inserted into `array` in order to maintain its sort order. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Array + * @param {Array} array The sorted array to inspect. + * @param {*} value The value to evaluate. + * @returns {number} Returns the index at which `value` should be inserted + * into `array`. + * @example + * + * _.sortedIndex([30, 50], 40); + * // => 1 + */ + function sortedIndex(array, value) { + return baseSortedIndex(array, value); + } + + /** + * This method is like `_.sortedIndex` except that it accepts `iteratee` + * which is invoked for `value` and each element of `array` to compute their + * sort ranking. The iteratee is invoked with one argument: (value). + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Array + * @param {Array} array The sorted array to inspect. + * @param {*} value The value to evaluate. + * @param {Function} [iteratee=_.identity] The iteratee invoked per element. + * @returns {number} Returns the index at which `value` should be inserted + * into `array`. + * @example + * + * var objects = [{ 'x': 4 }, { 'x': 5 }]; + * + * _.sortedIndexBy(objects, { 'x': 4 }, function(o) { return o.x; }); + * // => 0 + * + * // The `_.property` iteratee shorthand. + * _.sortedIndexBy(objects, { 'x': 4 }, 'x'); + * // => 0 + */ + function sortedIndexBy(array, value, iteratee) { + return baseSortedIndexBy(array, value, getIteratee(iteratee, 2)); + } + + /** + * This method is like `_.indexOf` except that it performs a binary + * search on a sorted `array`. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Array + * @param {Array} array The array to inspect. + * @param {*} value The value to search for. + * @returns {number} Returns the index of the matched value, else `-1`. + * @example + * + * _.sortedIndexOf([4, 5, 5, 5, 6], 5); + * // => 1 + */ + function sortedIndexOf(array, value) { + var length = array == null ? 0 : array.length; + if (length) { + var index = baseSortedIndex(array, value); + if (index < length && eq(array[index], value)) { + return index; + } + } + return -1; + } + + /** + * This method is like `_.sortedIndex` except that it returns the highest + * index at which `value` should be inserted into `array` in order to + * maintain its sort order. + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category Array + * @param {Array} array The sorted array to inspect. + * @param {*} value The value to evaluate. + * @returns {number} Returns the index at which `value` should be inserted + * into `array`. + * @example + * + * _.sortedLastIndex([4, 5, 5, 5, 6], 5); + * // => 4 + */ + function sortedLastIndex(array, value) { + return baseSortedIndex(array, value, true); + } + + /** + * This method is like `_.sortedLastIndex` except that it accepts `iteratee` + * which is invoked for `value` and each element of `array` to compute their + * sort ranking. The iteratee is invoked with one argument: (value). + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Array + * @param {Array} array The sorted array to inspect. + * @param {*} value The value to evaluate. + * @param {Function} [iteratee=_.identity] The iteratee invoked per element. + * @returns {number} Returns the index at which `value` should be inserted + * into `array`. + * @example + * + * var objects = [{ 'x': 4 }, { 'x': 5 }]; + * + * _.sortedLastIndexBy(objects, { 'x': 4 }, function(o) { return o.x; }); + * // => 1 + * + * // The `_.property` iteratee shorthand. + * _.sortedLastIndexBy(objects, { 'x': 4 }, 'x'); + * // => 1 + */ + function sortedLastIndexBy(array, value, iteratee) { + return baseSortedIndexBy(array, value, getIteratee(iteratee, 2), true); + } + + /** + * This method is like `_.lastIndexOf` except that it performs a binary + * search on a sorted `array`. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Array + * @param {Array} array The array to inspect. + * @param {*} value The value to search for. + * @returns {number} Returns the index of the matched value, else `-1`. + * @example + * + * _.sortedLastIndexOf([4, 5, 5, 5, 6], 5); + * // => 3 + */ + function sortedLastIndexOf(array, value) { + var length = array == null ? 0 : array.length; + if (length) { + var index = baseSortedIndex(array, value, true) - 1; + if (eq(array[index], value)) { + return index; + } + } + return -1; + } + + /** + * This method is like `_.uniq` except that it's designed and optimized + * for sorted arrays. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Array + * @param {Array} array The array to inspect. + * @returns {Array} Returns the new duplicate free array. + * @example + * + * _.sortedUniq([1, 1, 2]); + * // => [1, 2] + */ + function sortedUniq(array) { + return (array && array.length) + ? baseSortedUniq(array) + : []; + } + + /** + * This method is like `_.uniqBy` except that it's designed and optimized + * for sorted arrays. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Array + * @param {Array} array The array to inspect. + * @param {Function} [iteratee] The iteratee invoked per element. + * @returns {Array} Returns the new duplicate free array. + * @example + * + * _.sortedUniqBy([1.1, 1.2, 2.3, 2.4], Math.floor); + * // => [1.1, 2.3] + */ + function sortedUniqBy(array, iteratee) { + return (array && array.length) + ? baseSortedUniq(array, getIteratee(iteratee, 2)) + : []; + } + + /** + * Gets all but the first element of `array`. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Array + * @param {Array} array The array to query. + * @returns {Array} Returns the slice of `array`. + * @example + * + * _.tail([1, 2, 3]); + * // => [2, 3] + */ + function tail(array) { + var length = array == null ? 0 : array.length; + return length ? baseSlice(array, 1, length) : []; + } + + /** + * Creates a slice of `array` with `n` elements taken from the beginning. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Array + * @param {Array} array The array to query. + * @param {number} [n=1] The number of elements to take. + * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`. + * @returns {Array} Returns the slice of `array`. + * @example + * + * _.take([1, 2, 3]); + * // => [1] + * + * _.take([1, 2, 3], 2); + * // => [1, 2] + * + * _.take([1, 2, 3], 5); + * // => [1, 2, 3] + * + * _.take([1, 2, 3], 0); + * // => [] + */ + function take(array, n, guard) { + if (!(array && array.length)) { + return []; + } + n = (guard || n === undefined) ? 1 : toInteger(n); + return baseSlice(array, 0, n < 0 ? 0 : n); + } + + /** + * Creates a slice of `array` with `n` elements taken from the end. + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category Array + * @param {Array} array The array to query. + * @param {number} [n=1] The number of elements to take. + * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`. + * @returns {Array} Returns the slice of `array`. + * @example + * + * _.takeRight([1, 2, 3]); + * // => [3] + * + * _.takeRight([1, 2, 3], 2); + * // => [2, 3] + * + * _.takeRight([1, 2, 3], 5); + * // => [1, 2, 3] + * + * _.takeRight([1, 2, 3], 0); + * // => [] + */ + function takeRight(array, n, guard) { + var length = array == null ? 0 : array.length; + if (!length) { + return []; + } + n = (guard || n === undefined) ? 1 : toInteger(n); + n = length - n; + return baseSlice(array, n < 0 ? 0 : n, length); + } + + /** + * Creates a slice of `array` with elements taken from the end. Elements are + * taken until `predicate` returns falsey. The predicate is invoked with + * three arguments: (value, index, array). + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category Array + * @param {Array} array The array to query. + * @param {Function} [predicate=_.identity] The function invoked per iteration. + * @returns {Array} Returns the slice of `array`. + * @example + * + * var users = [ + * { 'user': 'barney', 'active': true }, + * { 'user': 'fred', 'active': false }, + * { 'user': 'pebbles', 'active': false } + * ]; + * + * _.takeRightWhile(users, function(o) { return !o.active; }); + * // => objects for ['fred', 'pebbles'] + * + * // The `_.matches` iteratee shorthand. + * _.takeRightWhile(users, { 'user': 'pebbles', 'active': false }); + * // => objects for ['pebbles'] + * + * // The `_.matchesProperty` iteratee shorthand. + * _.takeRightWhile(users, ['active', false]); + * // => objects for ['fred', 'pebbles'] + * + * // The `_.property` iteratee shorthand. + * _.takeRightWhile(users, 'active'); + * // => [] + */ + function takeRightWhile(array, predicate) { + return (array && array.length) + ? baseWhile(array, getIteratee(predicate, 3), false, true) + : []; + } + + /** + * Creates a slice of `array` with elements taken from the beginning. Elements + * are taken until `predicate` returns falsey. The predicate is invoked with + * three arguments: (value, index, array). + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category Array + * @param {Array} array The array to query. + * @param {Function} [predicate=_.identity] The function invoked per iteration. + * @returns {Array} Returns the slice of `array`. + * @example + * + * var users = [ + * { 'user': 'barney', 'active': false }, + * { 'user': 'fred', 'active': false }, + * { 'user': 'pebbles', 'active': true } + * ]; + * + * _.takeWhile(users, function(o) { return !o.active; }); + * // => objects for ['barney', 'fred'] + * + * // The `_.matches` iteratee shorthand. + * _.takeWhile(users, { 'user': 'barney', 'active': false }); + * // => objects for ['barney'] + * + * // The `_.matchesProperty` iteratee shorthand. + * _.takeWhile(users, ['active', false]); + * // => objects for ['barney', 'fred'] + * + * // The `_.property` iteratee shorthand. + * _.takeWhile(users, 'active'); + * // => [] + */ + function takeWhile(array, predicate) { + return (array && array.length) + ? baseWhile(array, getIteratee(predicate, 3)) + : []; + } + + /** + * Creates an array of unique values, in order, from all given arrays using + * [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero) + * for equality comparisons. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Array + * @param {...Array} [arrays] The arrays to inspect. + * @returns {Array} Returns the new array of combined values. + * @example + * + * _.union([2], [1, 2]); + * // => [2, 1] + */ + var union = baseRest(function(arrays) { + return baseUniq(baseFlatten(arrays, 1, isArrayLikeObject, true)); + }); + + /** + * This method is like `_.union` except that it accepts `iteratee` which is + * invoked for each element of each `arrays` to generate the criterion by + * which uniqueness is computed. Result values are chosen from the first + * array in which the value occurs. The iteratee is invoked with one argument: + * (value). + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Array + * @param {...Array} [arrays] The arrays to inspect. + * @param {Function} [iteratee=_.identity] The iteratee invoked per element. + * @returns {Array} Returns the new array of combined values. + * @example + * + * _.unionBy([2.1], [1.2, 2.3], Math.floor); + * // => [2.1, 1.2] + * + * // The `_.property` iteratee shorthand. + * _.unionBy([{ 'x': 1 }], [{ 'x': 2 }, { 'x': 1 }], 'x'); + * // => [{ 'x': 1 }, { 'x': 2 }] + */ + var unionBy = baseRest(function(arrays) { + var iteratee = last(arrays); + if (isArrayLikeObject(iteratee)) { + iteratee = undefined; + } + return baseUniq(baseFlatten(arrays, 1, isArrayLikeObject, true), getIteratee(iteratee, 2)); + }); + + /** + * This method is like `_.union` except that it accepts `comparator` which + * is invoked to compare elements of `arrays`. Result values are chosen from + * the first array in which the value occurs. The comparator is invoked + * with two arguments: (arrVal, othVal). + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Array + * @param {...Array} [arrays] The arrays to inspect. + * @param {Function} [comparator] The comparator invoked per element. + * @returns {Array} Returns the new array of combined values. + * @example + * + * var objects = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }]; + * var others = [{ 'x': 1, 'y': 1 }, { 'x': 1, 'y': 2 }]; + * + * _.unionWith(objects, others, _.isEqual); + * // => [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }, { 'x': 1, 'y': 1 }] + */ + var unionWith = baseRest(function(arrays) { + var comparator = last(arrays); + comparator = typeof comparator == 'function' ? comparator : undefined; + return baseUniq(baseFlatten(arrays, 1, isArrayLikeObject, true), undefined, comparator); + }); + + /** + * Creates a duplicate-free version of an array, using + * [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero) + * for equality comparisons, in which only the first occurrence of each element + * is kept. The order of result values is determined by the order they occur + * in the array. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Array + * @param {Array} array The array to inspect. + * @returns {Array} Returns the new duplicate free array. + * @example + * + * _.uniq([2, 1, 2]); + * // => [2, 1] + */ + function uniq(array) { + return (array && array.length) ? baseUniq(array) : []; + } + + /** + * This method is like `_.uniq` except that it accepts `iteratee` which is + * invoked for each element in `array` to generate the criterion by which + * uniqueness is computed. The order of result values is determined by the + * order they occur in the array. The iteratee is invoked with one argument: + * (value). + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Array + * @param {Array} array The array to inspect. + * @param {Function} [iteratee=_.identity] The iteratee invoked per element. + * @returns {Array} Returns the new duplicate free array. + * @example + * + * _.uniqBy([2.1, 1.2, 2.3], Math.floor); + * // => [2.1, 1.2] + * + * // The `_.property` iteratee shorthand. + * _.uniqBy([{ 'x': 1 }, { 'x': 2 }, { 'x': 1 }], 'x'); + * // => [{ 'x': 1 }, { 'x': 2 }] + */ + function uniqBy(array, iteratee) { + return (array && array.length) ? baseUniq(array, getIteratee(iteratee, 2)) : []; + } + + /** + * This method is like `_.uniq` except that it accepts `comparator` which + * is invoked to compare elements of `array`. The order of result values is + * determined by the order they occur in the array.The comparator is invoked + * with two arguments: (arrVal, othVal). + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Array + * @param {Array} array The array to inspect. + * @param {Function} [comparator] The comparator invoked per element. + * @returns {Array} Returns the new duplicate free array. + * @example + * + * var objects = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }, { 'x': 1, 'y': 2 }]; + * + * _.uniqWith(objects, _.isEqual); + * // => [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }] + */ + function uniqWith(array, comparator) { + comparator = typeof comparator == 'function' ? comparator : undefined; + return (array && array.length) ? baseUniq(array, undefined, comparator) : []; + } + + /** + * This method is like `_.zip` except that it accepts an array of grouped + * elements and creates an array regrouping the elements to their pre-zip + * configuration. + * + * @static + * @memberOf _ + * @since 1.2.0 + * @category Array + * @param {Array} array The array of grouped elements to process. + * @returns {Array} Returns the new array of regrouped elements. + * @example + * + * var zipped = _.zip(['a', 'b'], [1, 2], [true, false]); + * // => [['a', 1, true], ['b', 2, false]] + * + * _.unzip(zipped); + * // => [['a', 'b'], [1, 2], [true, false]] + */ + function unzip(array) { + if (!(array && array.length)) { + return []; + } + var length = 0; + array = arrayFilter(array, function(group) { + if (isArrayLikeObject(group)) { + length = nativeMax(group.length, length); + return true; + } + }); + return baseTimes(length, function(index) { + return arrayMap(array, baseProperty(index)); + }); + } + + /** + * This method is like `_.unzip` except that it accepts `iteratee` to specify + * how regrouped values should be combined. The iteratee is invoked with the + * elements of each group: (...group). + * + * @static + * @memberOf _ + * @since 3.8.0 + * @category Array + * @param {Array} array The array of grouped elements to process. + * @param {Function} [iteratee=_.identity] The function to combine + * regrouped values. + * @returns {Array} Returns the new array of regrouped elements. + * @example + * + * var zipped = _.zip([1, 2], [10, 20], [100, 200]); + * // => [[1, 10, 100], [2, 20, 200]] + * + * _.unzipWith(zipped, _.add); + * // => [3, 30, 300] + */ + function unzipWith(array, iteratee) { + if (!(array && array.length)) { + return []; + } + var result = unzip(array); + if (iteratee == null) { + return result; + } + return arrayMap(result, function(group) { + return apply(iteratee, undefined, group); + }); + } + + /** + * Creates an array excluding all given values using + * [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero) + * for equality comparisons. + * + * **Note:** Unlike `_.pull`, this method returns a new array. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Array + * @param {Array} array The array to inspect. + * @param {...*} [values] The values to exclude. + * @returns {Array} Returns the new array of filtered values. + * @see _.difference, _.xor + * @example + * + * _.without([2, 1, 2, 3], 1, 2); + * // => [3] + */ + var without = baseRest(function(array, values) { + return isArrayLikeObject(array) + ? baseDifference(array, values) + : []; + }); + + /** + * Creates an array of unique values that is the + * [symmetric difference](https://en.wikipedia.org/wiki/Symmetric_difference) + * of the given arrays. The order of result values is determined by the order + * they occur in the arrays. + * + * @static + * @memberOf _ + * @since 2.4.0 + * @category Array + * @param {...Array} [arrays] The arrays to inspect. + * @returns {Array} Returns the new array of filtered values. + * @see _.difference, _.without + * @example + * + * _.xor([2, 1], [2, 3]); + * // => [1, 3] + */ + var xor = baseRest(function(arrays) { + return baseXor(arrayFilter(arrays, isArrayLikeObject)); + }); + + /** + * This method is like `_.xor` except that it accepts `iteratee` which is + * invoked for each element of each `arrays` to generate the criterion by + * which by which they're compared. The order of result values is determined + * by the order they occur in the arrays. The iteratee is invoked with one + * argument: (value). + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Array + * @param {...Array} [arrays] The arrays to inspect. + * @param {Function} [iteratee=_.identity] The iteratee invoked per element. + * @returns {Array} Returns the new array of filtered values. + * @example + * + * _.xorBy([2.1, 1.2], [2.3, 3.4], Math.floor); + * // => [1.2, 3.4] + * + * // The `_.property` iteratee shorthand. + * _.xorBy([{ 'x': 1 }], [{ 'x': 2 }, { 'x': 1 }], 'x'); + * // => [{ 'x': 2 }] + */ + var xorBy = baseRest(function(arrays) { + var iteratee = last(arrays); + if (isArrayLikeObject(iteratee)) { + iteratee = undefined; + } + return baseXor(arrayFilter(arrays, isArrayLikeObject), getIteratee(iteratee, 2)); + }); + + /** + * This method is like `_.xor` except that it accepts `comparator` which is + * invoked to compare elements of `arrays`. The order of result values is + * determined by the order they occur in the arrays. The comparator is invoked + * with two arguments: (arrVal, othVal). + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Array + * @param {...Array} [arrays] The arrays to inspect. + * @param {Function} [comparator] The comparator invoked per element. + * @returns {Array} Returns the new array of filtered values. + * @example + * + * var objects = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }]; + * var others = [{ 'x': 1, 'y': 1 }, { 'x': 1, 'y': 2 }]; + * + * _.xorWith(objects, others, _.isEqual); + * // => [{ 'x': 2, 'y': 1 }, { 'x': 1, 'y': 1 }] + */ + var xorWith = baseRest(function(arrays) { + var comparator = last(arrays); + comparator = typeof comparator == 'function' ? comparator : undefined; + return baseXor(arrayFilter(arrays, isArrayLikeObject), undefined, comparator); + }); + + /** + * Creates an array of grouped elements, the first of which contains the + * first elements of the given arrays, the second of which contains the + * second elements of the given arrays, and so on. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Array + * @param {...Array} [arrays] The arrays to process. + * @returns {Array} Returns the new array of grouped elements. + * @example + * + * _.zip(['a', 'b'], [1, 2], [true, false]); + * // => [['a', 1, true], ['b', 2, false]] + */ + var zip = baseRest(unzip); + + /** + * This method is like `_.fromPairs` except that it accepts two arrays, + * one of property identifiers and one of corresponding values. + * + * @static + * @memberOf _ + * @since 0.4.0 + * @category Array + * @param {Array} [props=[]] The property identifiers. + * @param {Array} [values=[]] The property values. + * @returns {Object} Returns the new object. + * @example + * + * _.zipObject(['a', 'b'], [1, 2]); + * // => { 'a': 1, 'b': 2 } + */ + function zipObject(props, values) { + return baseZipObject(props || [], values || [], assignValue); + } + + /** + * This method is like `_.zipObject` except that it supports property paths. + * + * @static + * @memberOf _ + * @since 4.1.0 + * @category Array + * @param {Array} [props=[]] The property identifiers. + * @param {Array} [values=[]] The property values. + * @returns {Object} Returns the new object. + * @example + * + * _.zipObjectDeep(['a.b[0].c', 'a.b[1].d'], [1, 2]); + * // => { 'a': { 'b': [{ 'c': 1 }, { 'd': 2 }] } } + */ + function zipObjectDeep(props, values) { + return baseZipObject(props || [], values || [], baseSet); + } + + /** + * This method is like `_.zip` except that it accepts `iteratee` to specify + * how grouped values should be combined. The iteratee is invoked with the + * elements of each group: (...group). + * + * @static + * @memberOf _ + * @since 3.8.0 + * @category Array + * @param {...Array} [arrays] The arrays to process. + * @param {Function} [iteratee=_.identity] The function to combine + * grouped values. + * @returns {Array} Returns the new array of grouped elements. + * @example + * + * _.zipWith([1, 2], [10, 20], [100, 200], function(a, b, c) { + * return a + b + c; + * }); + * // => [111, 222] + */ + var zipWith = baseRest(function(arrays) { + var length = arrays.length, + iteratee = length > 1 ? arrays[length - 1] : undefined; + + iteratee = typeof iteratee == 'function' ? (arrays.pop(), iteratee) : undefined; + return unzipWith(arrays, iteratee); + }); + + /*------------------------------------------------------------------------*/ + + /** + * Creates a `lodash` wrapper instance that wraps `value` with explicit method + * chain sequences enabled. The result of such sequences must be unwrapped + * with `_#value`. + * + * @static + * @memberOf _ + * @since 1.3.0 + * @category Seq + * @param {*} value The value to wrap. + * @returns {Object} Returns the new `lodash` wrapper instance. + * @example + * + * var users = [ + * { 'user': 'barney', 'age': 36 }, + * { 'user': 'fred', 'age': 40 }, + * { 'user': 'pebbles', 'age': 1 } + * ]; + * + * var youngest = _ + * .chain(users) + * .sortBy('age') + * .map(function(o) { + * return o.user + ' is ' + o.age; + * }) + * .head() + * .value(); + * // => 'pebbles is 1' + */ + function chain(value) { + var result = lodash(value); + result.__chain__ = true; + return result; + } + + /** + * This method invokes `interceptor` and returns `value`. The interceptor + * is invoked with one argument; (value). The purpose of this method is to + * "tap into" a method chain sequence in order to modify intermediate results. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Seq + * @param {*} value The value to provide to `interceptor`. + * @param {Function} interceptor The function to invoke. + * @returns {*} Returns `value`. + * @example + * + * _([1, 2, 3]) + * .tap(function(array) { + * // Mutate input array. + * array.pop(); + * }) + * .reverse() + * .value(); + * // => [2, 1] + */ + function tap(value, interceptor) { + interceptor(value); + return value; + } + + /** + * This method is like `_.tap` except that it returns the result of `interceptor`. + * The purpose of this method is to "pass thru" values replacing intermediate + * results in a method chain sequence. + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category Seq + * @param {*} value The value to provide to `interceptor`. + * @param {Function} interceptor The function to invoke. + * @returns {*} Returns the result of `interceptor`. + * @example + * + * _(' abc ') + * .chain() + * .trim() + * .thru(function(value) { + * return [value]; + * }) + * .value(); + * // => ['abc'] + */ + function thru(value, interceptor) { + return interceptor(value); + } + + /** + * This method is the wrapper version of `_.at`. + * + * @name at + * @memberOf _ + * @since 1.0.0 + * @category Seq + * @param {...(string|string[])} [paths] The property paths to pick. + * @returns {Object} Returns the new `lodash` wrapper instance. + * @example + * + * var object = { 'a': [{ 'b': { 'c': 3 } }, 4] }; + * + * _(object).at(['a[0].b.c', 'a[1]']).value(); + * // => [3, 4] + */ + var wrapperAt = flatRest(function(paths) { + var length = paths.length, + start = length ? paths[0] : 0, + value = this.__wrapped__, + interceptor = function(object) { return baseAt(object, paths); }; + + if (length > 1 || this.__actions__.length || + !(value instanceof LazyWrapper) || !isIndex(start)) { + return this.thru(interceptor); + } + value = value.slice(start, +start + (length ? 1 : 0)); + value.__actions__.push({ + 'func': thru, + 'args': [interceptor], + 'thisArg': undefined + }); + return new LodashWrapper(value, this.__chain__).thru(function(array) { + if (length && !array.length) { + array.push(undefined); + } + return array; + }); + }); + + /** + * Creates a `lodash` wrapper instance with explicit method chain sequences enabled. + * + * @name chain + * @memberOf _ + * @since 0.1.0 + * @category Seq + * @returns {Object} Returns the new `lodash` wrapper instance. + * @example + * + * var users = [ + * { 'user': 'barney', 'age': 36 }, + * { 'user': 'fred', 'age': 40 } + * ]; + * + * // A sequence without explicit chaining. + * _(users).head(); + * // => { 'user': 'barney', 'age': 36 } + * + * // A sequence with explicit chaining. + * _(users) + * .chain() + * .head() + * .pick('user') + * .value(); + * // => { 'user': 'barney' } + */ + function wrapperChain() { + return chain(this); + } + + /** + * Executes the chain sequence and returns the wrapped result. + * + * @name commit + * @memberOf _ + * @since 3.2.0 + * @category Seq + * @returns {Object} Returns the new `lodash` wrapper instance. + * @example + * + * var array = [1, 2]; + * var wrapped = _(array).push(3); + * + * console.log(array); + * // => [1, 2] + * + * wrapped = wrapped.commit(); + * console.log(array); + * // => [1, 2, 3] + * + * wrapped.last(); + * // => 3 + * + * console.log(array); + * // => [1, 2, 3] + */ + function wrapperCommit() { + return new LodashWrapper(this.value(), this.__chain__); + } + + /** + * Gets the next value on a wrapped object following the + * [iterator protocol](https://mdn.io/iteration_protocols#iterator). + * + * @name next + * @memberOf _ + * @since 4.0.0 + * @category Seq + * @returns {Object} Returns the next iterator value. + * @example + * + * var wrapped = _([1, 2]); + * + * wrapped.next(); + * // => { 'done': false, 'value': 1 } + * + * wrapped.next(); + * // => { 'done': false, 'value': 2 } + * + * wrapped.next(); + * // => { 'done': true, 'value': undefined } + */ + function wrapperNext() { + if (this.__values__ === undefined) { + this.__values__ = toArray(this.value()); + } + var done = this.__index__ >= this.__values__.length, + value = done ? undefined : this.__values__[this.__index__++]; + + return { 'done': done, 'value': value }; + } + + /** + * Enables the wrapper to be iterable. + * + * @name Symbol.iterator + * @memberOf _ + * @since 4.0.0 + * @category Seq + * @returns {Object} Returns the wrapper object. + * @example + * + * var wrapped = _([1, 2]); + * + * wrapped[Symbol.iterator]() === wrapped; + * // => true + * + * Array.from(wrapped); + * // => [1, 2] + */ + function wrapperToIterator() { + return this; + } + + /** + * Creates a clone of the chain sequence planting `value` as the wrapped value. + * + * @name plant + * @memberOf _ + * @since 3.2.0 + * @category Seq + * @param {*} value The value to plant. + * @returns {Object} Returns the new `lodash` wrapper instance. + * @example + * + * function square(n) { + * return n * n; + * } + * + * var wrapped = _([1, 2]).map(square); + * var other = wrapped.plant([3, 4]); + * + * other.value(); + * // => [9, 16] + * + * wrapped.value(); + * // => [1, 4] + */ + function wrapperPlant(value) { + var result, + parent = this; + + while (parent instanceof baseLodash) { + var clone = wrapperClone(parent); + clone.__index__ = 0; + clone.__values__ = undefined; + if (result) { + previous.__wrapped__ = clone; + } else { + result = clone; + } + var previous = clone; + parent = parent.__wrapped__; + } + previous.__wrapped__ = value; + return result; + } + + /** + * This method is the wrapper version of `_.reverse`. + * + * **Note:** This method mutates the wrapped array. + * + * @name reverse + * @memberOf _ + * @since 0.1.0 + * @category Seq + * @returns {Object} Returns the new `lodash` wrapper instance. + * @example + * + * var array = [1, 2, 3]; + * + * _(array).reverse().value() + * // => [3, 2, 1] + * + * console.log(array); + * // => [3, 2, 1] + */ + function wrapperReverse() { + var value = this.__wrapped__; + if (value instanceof LazyWrapper) { + var wrapped = value; + if (this.__actions__.length) { + wrapped = new LazyWrapper(this); + } + wrapped = wrapped.reverse(); + wrapped.__actions__.push({ + 'func': thru, + 'args': [reverse], + 'thisArg': undefined + }); + return new LodashWrapper(wrapped, this.__chain__); + } + return this.thru(reverse); + } + + /** + * Executes the chain sequence to resolve the unwrapped value. + * + * @name value + * @memberOf _ + * @since 0.1.0 + * @alias toJSON, valueOf + * @category Seq + * @returns {*} Returns the resolved unwrapped value. + * @example + * + * _([1, 2, 3]).value(); + * // => [1, 2, 3] + */ + function wrapperValue() { + return baseWrapperValue(this.__wrapped__, this.__actions__); + } + + /*------------------------------------------------------------------------*/ + + /** + * Creates an object composed of keys generated from the results of running + * each element of `collection` thru `iteratee`. The corresponding value of + * each key is the number of times the key was returned by `iteratee`. The + * iteratee is invoked with one argument: (value). + * + * @static + * @memberOf _ + * @since 0.5.0 + * @category Collection + * @param {Array|Object} collection The collection to iterate over. + * @param {Function} [iteratee=_.identity] The iteratee to transform keys. + * @returns {Object} Returns the composed aggregate object. + * @example + * + * _.countBy([6.1, 4.2, 6.3], Math.floor); + * // => { '4': 1, '6': 2 } + * + * // The `_.property` iteratee shorthand. + * _.countBy(['one', 'two', 'three'], 'length'); + * // => { '3': 2, '5': 1 } + */ + var countBy = createAggregator(function(result, value, key) { + if (hasOwnProperty.call(result, key)) { + ++result[key]; + } else { + baseAssignValue(result, key, 1); + } + }); + + /** + * Checks if `predicate` returns truthy for **all** elements of `collection`. + * Iteration is stopped once `predicate` returns falsey. The predicate is + * invoked with three arguments: (value, index|key, collection). + * + * **Note:** This method returns `true` for + * [empty collections](https://en.wikipedia.org/wiki/Empty_set) because + * [everything is true](https://en.wikipedia.org/wiki/Vacuous_truth) of + * elements of empty collections. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Collection + * @param {Array|Object} collection The collection to iterate over. + * @param {Function} [predicate=_.identity] The function invoked per iteration. + * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`. + * @returns {boolean} Returns `true` if all elements pass the predicate check, + * else `false`. + * @example + * + * _.every([true, 1, null, 'yes'], Boolean); + * // => false + * + * var users = [ + * { 'user': 'barney', 'age': 36, 'active': false }, + * { 'user': 'fred', 'age': 40, 'active': false } + * ]; + * + * // The `_.matches` iteratee shorthand. + * _.every(users, { 'user': 'barney', 'active': false }); + * // => false + * + * // The `_.matchesProperty` iteratee shorthand. + * _.every(users, ['active', false]); + * // => true + * + * // The `_.property` iteratee shorthand. + * _.every(users, 'active'); + * // => false + */ + function every(collection, predicate, guard) { + var func = isArray(collection) ? arrayEvery : baseEvery; + if (guard && isIterateeCall(collection, predicate, guard)) { + predicate = undefined; + } + return func(collection, getIteratee(predicate, 3)); + } + + /** + * Iterates over elements of `collection`, returning an array of all elements + * `predicate` returns truthy for. The predicate is invoked with three + * arguments: (value, index|key, collection). + * + * **Note:** Unlike `_.remove`, this method returns a new array. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Collection + * @param {Array|Object} collection The collection to iterate over. + * @param {Function} [predicate=_.identity] The function invoked per iteration. + * @returns {Array} Returns the new filtered array. + * @see _.reject + * @example + * + * var users = [ + * { 'user': 'barney', 'age': 36, 'active': true }, + * { 'user': 'fred', 'age': 40, 'active': false } + * ]; + * + * _.filter(users, function(o) { return !o.active; }); + * // => objects for ['fred'] + * + * // The `_.matches` iteratee shorthand. + * _.filter(users, { 'age': 36, 'active': true }); + * // => objects for ['barney'] + * + * // The `_.matchesProperty` iteratee shorthand. + * _.filter(users, ['active', false]); + * // => objects for ['fred'] + * + * // The `_.property` iteratee shorthand. + * _.filter(users, 'active'); + * // => objects for ['barney'] + */ + function filter(collection, predicate) { + var func = isArray(collection) ? arrayFilter : baseFilter; + return func(collection, getIteratee(predicate, 3)); + } + + /** + * Iterates over elements of `collection`, returning the first element + * `predicate` returns truthy for. The predicate is invoked with three + * arguments: (value, index|key, collection). + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Collection + * @param {Array|Object} collection The collection to inspect. + * @param {Function} [predicate=_.identity] The function invoked per iteration. + * @param {number} [fromIndex=0] The index to search from. + * @returns {*} Returns the matched element, else `undefined`. + * @example + * + * var users = [ + * { 'user': 'barney', 'age': 36, 'active': true }, + * { 'user': 'fred', 'age': 40, 'active': false }, + * { 'user': 'pebbles', 'age': 1, 'active': true } + * ]; + * + * _.find(users, function(o) { return o.age < 40; }); + * // => object for 'barney' + * + * // The `_.matches` iteratee shorthand. + * _.find(users, { 'age': 1, 'active': true }); + * // => object for 'pebbles' + * + * // The `_.matchesProperty` iteratee shorthand. + * _.find(users, ['active', false]); + * // => object for 'fred' + * + * // The `_.property` iteratee shorthand. + * _.find(users, 'active'); + * // => object for 'barney' + */ + var find = createFind(findIndex); + + /** + * This method is like `_.find` except that it iterates over elements of + * `collection` from right to left. + * + * @static + * @memberOf _ + * @since 2.0.0 + * @category Collection + * @param {Array|Object} collection The collection to inspect. + * @param {Function} [predicate=_.identity] The function invoked per iteration. + * @param {number} [fromIndex=collection.length-1] The index to search from. + * @returns {*} Returns the matched element, else `undefined`. + * @example + * + * _.findLast([1, 2, 3, 4], function(n) { + * return n % 2 == 1; + * }); + * // => 3 + */ + var findLast = createFind(findLastIndex); + + /** + * Creates a flattened array of values by running each element in `collection` + * thru `iteratee` and flattening the mapped results. The iteratee is invoked + * with three arguments: (value, index|key, collection). + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Collection + * @param {Array|Object} collection The collection to iterate over. + * @param {Function} [iteratee=_.identity] The function invoked per iteration. + * @returns {Array} Returns the new flattened array. + * @example + * + * function duplicate(n) { + * return [n, n]; + * } + * + * _.flatMap([1, 2], duplicate); + * // => [1, 1, 2, 2] + */ + function flatMap(collection, iteratee) { + return baseFlatten(map(collection, iteratee), 1); + } + + /** + * This method is like `_.flatMap` except that it recursively flattens the + * mapped results. + * + * @static + * @memberOf _ + * @since 4.7.0 + * @category Collection + * @param {Array|Object} collection The collection to iterate over. + * @param {Function} [iteratee=_.identity] The function invoked per iteration. + * @returns {Array} Returns the new flattened array. + * @example + * + * function duplicate(n) { + * return [[[n, n]]]; + * } + * + * _.flatMapDeep([1, 2], duplicate); + * // => [1, 1, 2, 2] + */ + function flatMapDeep(collection, iteratee) { + return baseFlatten(map(collection, iteratee), INFINITY); + } + + /** + * This method is like `_.flatMap` except that it recursively flattens the + * mapped results up to `depth` times. + * + * @static + * @memberOf _ + * @since 4.7.0 + * @category Collection + * @param {Array|Object} collection The collection to iterate over. + * @param {Function} [iteratee=_.identity] The function invoked per iteration. + * @param {number} [depth=1] The maximum recursion depth. + * @returns {Array} Returns the new flattened array. + * @example + * + * function duplicate(n) { + * return [[[n, n]]]; + * } + * + * _.flatMapDepth([1, 2], duplicate, 2); + * // => [[1, 1], [2, 2]] + */ + function flatMapDepth(collection, iteratee, depth) { + depth = depth === undefined ? 1 : toInteger(depth); + return baseFlatten(map(collection, iteratee), depth); + } + + /** + * Iterates over elements of `collection` and invokes `iteratee` for each element. + * The iteratee is invoked with three arguments: (value, index|key, collection). + * Iteratee functions may exit iteration early by explicitly returning `false`. + * + * **Note:** As with other "Collections" methods, objects with a "length" + * property are iterated like arrays. To avoid this behavior use `_.forIn` + * or `_.forOwn` for object iteration. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @alias each + * @category Collection + * @param {Array|Object} collection The collection to iterate over. + * @param {Function} [iteratee=_.identity] The function invoked per iteration. + * @returns {Array|Object} Returns `collection`. + * @see _.forEachRight + * @example + * + * _.forEach([1, 2], function(value) { + * console.log(value); + * }); + * // => Logs `1` then `2`. + * + * _.forEach({ 'a': 1, 'b': 2 }, function(value, key) { + * console.log(key); + * }); + * // => Logs 'a' then 'b' (iteration order is not guaranteed). + */ + function forEach(collection, iteratee) { + var func = isArray(collection) ? arrayEach : baseEach; + return func(collection, getIteratee(iteratee, 3)); + } + + /** + * This method is like `_.forEach` except that it iterates over elements of + * `collection` from right to left. + * + * @static + * @memberOf _ + * @since 2.0.0 + * @alias eachRight + * @category Collection + * @param {Array|Object} collection The collection to iterate over. + * @param {Function} [iteratee=_.identity] The function invoked per iteration. + * @returns {Array|Object} Returns `collection`. + * @see _.forEach + * @example + * + * _.forEachRight([1, 2], function(value) { + * console.log(value); + * }); + * // => Logs `2` then `1`. + */ + function forEachRight(collection, iteratee) { + var func = isArray(collection) ? arrayEachRight : baseEachRight; + return func(collection, getIteratee(iteratee, 3)); + } + + /** + * Creates an object composed of keys generated from the results of running + * each element of `collection` thru `iteratee`. The order of grouped values + * is determined by the order they occur in `collection`. The corresponding + * value of each key is an array of elements responsible for generating the + * key. The iteratee is invoked with one argument: (value). + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Collection + * @param {Array|Object} collection The collection to iterate over. + * @param {Function} [iteratee=_.identity] The iteratee to transform keys. + * @returns {Object} Returns the composed aggregate object. + * @example + * + * _.groupBy([6.1, 4.2, 6.3], Math.floor); + * // => { '4': [4.2], '6': [6.1, 6.3] } + * + * // The `_.property` iteratee shorthand. + * _.groupBy(['one', 'two', 'three'], 'length'); + * // => { '3': ['one', 'two'], '5': ['three'] } + */ + var groupBy = createAggregator(function(result, value, key) { + if (hasOwnProperty.call(result, key)) { + result[key].push(value); + } else { + baseAssignValue(result, key, [value]); + } + }); + + /** + * Checks if `value` is in `collection`. If `collection` is a string, it's + * checked for a substring of `value`, otherwise + * [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero) + * is used for equality comparisons. If `fromIndex` is negative, it's used as + * the offset from the end of `collection`. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Collection + * @param {Array|Object|string} collection The collection to inspect. + * @param {*} value The value to search for. + * @param {number} [fromIndex=0] The index to search from. + * @param- {Object} [guard] Enables use as an iteratee for methods like `_.reduce`. + * @returns {boolean} Returns `true` if `value` is found, else `false`. + * @example + * + * _.includes([1, 2, 3], 1); + * // => true + * + * _.includes([1, 2, 3], 1, 2); + * // => false + * + * _.includes({ 'a': 1, 'b': 2 }, 1); + * // => true + * + * _.includes('abcd', 'bc'); + * // => true + */ + function includes(collection, value, fromIndex, guard) { + collection = isArrayLike(collection) ? collection : values(collection); + fromIndex = (fromIndex && !guard) ? toInteger(fromIndex) : 0; + + var length = collection.length; + if (fromIndex < 0) { + fromIndex = nativeMax(length + fromIndex, 0); + } + return isString(collection) + ? (fromIndex <= length && collection.indexOf(value, fromIndex) > -1) + : (!!length && baseIndexOf(collection, value, fromIndex) > -1); + } + + /** + * Invokes the method at `path` of each element in `collection`, returning + * an array of the results of each invoked method. Any additional arguments + * are provided to each invoked method. If `path` is a function, it's invoked + * for, and `this` bound to, each element in `collection`. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Collection + * @param {Array|Object} collection The collection to iterate over. + * @param {Array|Function|string} path The path of the method to invoke or + * the function invoked per iteration. + * @param {...*} [args] The arguments to invoke each method with. + * @returns {Array} Returns the array of results. + * @example + * + * _.invokeMap([[5, 1, 7], [3, 2, 1]], 'sort'); + * // => [[1, 5, 7], [1, 2, 3]] + * + * _.invokeMap([123, 456], String.prototype.split, ''); + * // => [['1', '2', '3'], ['4', '5', '6']] + */ + var invokeMap = baseRest(function(collection, path, args) { + var index = -1, + isFunc = typeof path == 'function', + result = isArrayLike(collection) ? Array(collection.length) : []; + + baseEach(collection, function(value) { + result[++index] = isFunc ? apply(path, value, args) : baseInvoke(value, path, args); + }); + return result; + }); + + /** + * Creates an object composed of keys generated from the results of running + * each element of `collection` thru `iteratee`. The corresponding value of + * each key is the last element responsible for generating the key. The + * iteratee is invoked with one argument: (value). + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Collection + * @param {Array|Object} collection The collection to iterate over. + * @param {Function} [iteratee=_.identity] The iteratee to transform keys. + * @returns {Object} Returns the composed aggregate object. + * @example + * + * var array = [ + * { 'dir': 'left', 'code': 97 }, + * { 'dir': 'right', 'code': 100 } + * ]; + * + * _.keyBy(array, function(o) { + * return String.fromCharCode(o.code); + * }); + * // => { 'a': { 'dir': 'left', 'code': 97 }, 'd': { 'dir': 'right', 'code': 100 } } + * + * _.keyBy(array, 'dir'); + * // => { 'left': { 'dir': 'left', 'code': 97 }, 'right': { 'dir': 'right', 'code': 100 } } + */ + var keyBy = createAggregator(function(result, value, key) { + baseAssignValue(result, key, value); + }); + + /** + * Creates an array of values by running each element in `collection` thru + * `iteratee`. The iteratee is invoked with three arguments: + * (value, index|key, collection). + * + * Many lodash methods are guarded to work as iteratees for methods like + * `_.every`, `_.filter`, `_.map`, `_.mapValues`, `_.reject`, and `_.some`. + * + * The guarded methods are: + * `ary`, `chunk`, `curry`, `curryRight`, `drop`, `dropRight`, `every`, + * `fill`, `invert`, `parseInt`, `random`, `range`, `rangeRight`, `repeat`, + * `sampleSize`, `slice`, `some`, `sortBy`, `split`, `take`, `takeRight`, + * `template`, `trim`, `trimEnd`, `trimStart`, and `words` + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Collection + * @param {Array|Object} collection The collection to iterate over. + * @param {Function} [iteratee=_.identity] The function invoked per iteration. + * @returns {Array} Returns the new mapped array. + * @example + * + * function square(n) { + * return n * n; + * } + * + * _.map([4, 8], square); + * // => [16, 64] + * + * _.map({ 'a': 4, 'b': 8 }, square); + * // => [16, 64] (iteration order is not guaranteed) + * + * var users = [ + * { 'user': 'barney' }, + * { 'user': 'fred' } + * ]; + * + * // The `_.property` iteratee shorthand. + * _.map(users, 'user'); + * // => ['barney', 'fred'] + */ + function map(collection, iteratee) { + var func = isArray(collection) ? arrayMap : baseMap; + return func(collection, getIteratee(iteratee, 3)); + } + + /** + * This method is like `_.sortBy` except that it allows specifying the sort + * orders of the iteratees to sort by. If `orders` is unspecified, all values + * are sorted in ascending order. Otherwise, specify an order of "desc" for + * descending or "asc" for ascending sort order of corresponding values. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Collection + * @param {Array|Object} collection The collection to iterate over. + * @param {Array[]|Function[]|Object[]|string[]} [iteratees=[_.identity]] + * The iteratees to sort by. + * @param {string[]} [orders] The sort orders of `iteratees`. + * @param- {Object} [guard] Enables use as an iteratee for methods like `_.reduce`. + * @returns {Array} Returns the new sorted array. + * @example + * + * var users = [ + * { 'user': 'fred', 'age': 48 }, + * { 'user': 'barney', 'age': 34 }, + * { 'user': 'fred', 'age': 40 }, + * { 'user': 'barney', 'age': 36 } + * ]; + * + * // Sort by `user` in ascending order and by `age` in descending order. + * _.orderBy(users, ['user', 'age'], ['asc', 'desc']); + * // => objects for [['barney', 36], ['barney', 34], ['fred', 48], ['fred', 40]] + */ + function orderBy(collection, iteratees, orders, guard) { + if (collection == null) { + return []; + } + if (!isArray(iteratees)) { + iteratees = iteratees == null ? [] : [iteratees]; + } + orders = guard ? undefined : orders; + if (!isArray(orders)) { + orders = orders == null ? [] : [orders]; + } + return baseOrderBy(collection, iteratees, orders); + } + + /** + * Creates an array of elements split into two groups, the first of which + * contains elements `predicate` returns truthy for, the second of which + * contains elements `predicate` returns falsey for. The predicate is + * invoked with one argument: (value). + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category Collection + * @param {Array|Object} collection The collection to iterate over. + * @param {Function} [predicate=_.identity] The function invoked per iteration. + * @returns {Array} Returns the array of grouped elements. + * @example + * + * var users = [ + * { 'user': 'barney', 'age': 36, 'active': false }, + * { 'user': 'fred', 'age': 40, 'active': true }, + * { 'user': 'pebbles', 'age': 1, 'active': false } + * ]; + * + * _.partition(users, function(o) { return o.active; }); + * // => objects for [['fred'], ['barney', 'pebbles']] + * + * // The `_.matches` iteratee shorthand. + * _.partition(users, { 'age': 1, 'active': false }); + * // => objects for [['pebbles'], ['barney', 'fred']] + * + * // The `_.matchesProperty` iteratee shorthand. + * _.partition(users, ['active', false]); + * // => objects for [['barney', 'pebbles'], ['fred']] + * + * // The `_.property` iteratee shorthand. + * _.partition(users, 'active'); + * // => objects for [['fred'], ['barney', 'pebbles']] + */ + var partition = createAggregator(function(result, value, key) { + result[key ? 0 : 1].push(value); + }, function() { return [[], []]; }); + + /** + * Reduces `collection` to a value which is the accumulated result of running + * each element in `collection` thru `iteratee`, where each successive + * invocation is supplied the return value of the previous. If `accumulator` + * is not given, the first element of `collection` is used as the initial + * value. The iteratee is invoked with four arguments: + * (accumulator, value, index|key, collection). + * + * Many lodash methods are guarded to work as iteratees for methods like + * `_.reduce`, `_.reduceRight`, and `_.transform`. + * + * The guarded methods are: + * `assign`, `defaults`, `defaultsDeep`, `includes`, `merge`, `orderBy`, + * and `sortBy` + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Collection + * @param {Array|Object} collection The collection to iterate over. + * @param {Function} [iteratee=_.identity] The function invoked per iteration. + * @param {*} [accumulator] The initial value. + * @returns {*} Returns the accumulated value. + * @see _.reduceRight + * @example + * + * _.reduce([1, 2], function(sum, n) { + * return sum + n; + * }, 0); + * // => 3 + * + * _.reduce({ 'a': 1, 'b': 2, 'c': 1 }, function(result, value, key) { + * (result[value] || (result[value] = [])).push(key); + * return result; + * }, {}); + * // => { '1': ['a', 'c'], '2': ['b'] } (iteration order is not guaranteed) + */ + function reduce(collection, iteratee, accumulator) { + var func = isArray(collection) ? arrayReduce : baseReduce, + initAccum = arguments.length < 3; + + return func(collection, getIteratee(iteratee, 4), accumulator, initAccum, baseEach); + } + + /** + * This method is like `_.reduce` except that it iterates over elements of + * `collection` from right to left. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Collection + * @param {Array|Object} collection The collection to iterate over. + * @param {Function} [iteratee=_.identity] The function invoked per iteration. + * @param {*} [accumulator] The initial value. + * @returns {*} Returns the accumulated value. + * @see _.reduce + * @example + * + * var array = [[0, 1], [2, 3], [4, 5]]; + * + * _.reduceRight(array, function(flattened, other) { + * return flattened.concat(other); + * }, []); + * // => [4, 5, 2, 3, 0, 1] + */ + function reduceRight(collection, iteratee, accumulator) { + var func = isArray(collection) ? arrayReduceRight : baseReduce, + initAccum = arguments.length < 3; + + return func(collection, getIteratee(iteratee, 4), accumulator, initAccum, baseEachRight); + } + + /** + * The opposite of `_.filter`; this method returns the elements of `collection` + * that `predicate` does **not** return truthy for. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Collection + * @param {Array|Object} collection The collection to iterate over. + * @param {Function} [predicate=_.identity] The function invoked per iteration. + * @returns {Array} Returns the new filtered array. + * @see _.filter + * @example + * + * var users = [ + * { 'user': 'barney', 'age': 36, 'active': false }, + * { 'user': 'fred', 'age': 40, 'active': true } + * ]; + * + * _.reject(users, function(o) { return !o.active; }); + * // => objects for ['fred'] + * + * // The `_.matches` iteratee shorthand. + * _.reject(users, { 'age': 40, 'active': true }); + * // => objects for ['barney'] + * + * // The `_.matchesProperty` iteratee shorthand. + * _.reject(users, ['active', false]); + * // => objects for ['fred'] + * + * // The `_.property` iteratee shorthand. + * _.reject(users, 'active'); + * // => objects for ['barney'] + */ + function reject(collection, predicate) { + var func = isArray(collection) ? arrayFilter : baseFilter; + return func(collection, negate(getIteratee(predicate, 3))); + } + + /** + * Gets a random element from `collection`. + * + * @static + * @memberOf _ + * @since 2.0.0 + * @category Collection + * @param {Array|Object} collection The collection to sample. + * @returns {*} Returns the random element. + * @example + * + * _.sample([1, 2, 3, 4]); + * // => 2 + */ + function sample(collection) { + var func = isArray(collection) ? arraySample : baseSample; + return func(collection); + } + + /** + * Gets `n` random elements at unique keys from `collection` up to the + * size of `collection`. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Collection + * @param {Array|Object} collection The collection to sample. + * @param {number} [n=1] The number of elements to sample. + * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`. + * @returns {Array} Returns the random elements. + * @example + * + * _.sampleSize([1, 2, 3], 2); + * // => [3, 1] + * + * _.sampleSize([1, 2, 3], 4); + * // => [2, 3, 1] + */ + function sampleSize(collection, n, guard) { + if ((guard ? isIterateeCall(collection, n, guard) : n === undefined)) { + n = 1; + } else { + n = toInteger(n); + } + var func = isArray(collection) ? arraySampleSize : baseSampleSize; + return func(collection, n); + } + + /** + * Creates an array of shuffled values, using a version of the + * [Fisher-Yates shuffle](https://en.wikipedia.org/wiki/Fisher-Yates_shuffle). + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Collection + * @param {Array|Object} collection The collection to shuffle. + * @returns {Array} Returns the new shuffled array. + * @example + * + * _.shuffle([1, 2, 3, 4]); + * // => [4, 1, 3, 2] + */ + function shuffle(collection) { + var func = isArray(collection) ? arrayShuffle : baseShuffle; + return func(collection); + } + + /** + * Gets the size of `collection` by returning its length for array-like + * values or the number of own enumerable string keyed properties for objects. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Collection + * @param {Array|Object|string} collection The collection to inspect. + * @returns {number} Returns the collection size. + * @example + * + * _.size([1, 2, 3]); + * // => 3 + * + * _.size({ 'a': 1, 'b': 2 }); + * // => 2 + * + * _.size('pebbles'); + * // => 7 + */ + function size(collection) { + if (collection == null) { + return 0; + } + if (isArrayLike(collection)) { + return isString(collection) ? stringSize(collection) : collection.length; + } + var tag = getTag(collection); + if (tag == mapTag || tag == setTag) { + return collection.size; + } + return baseKeys(collection).length; + } + + /** + * Checks if `predicate` returns truthy for **any** element of `collection`. + * Iteration is stopped once `predicate` returns truthy. The predicate is + * invoked with three arguments: (value, index|key, collection). + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Collection + * @param {Array|Object} collection The collection to iterate over. + * @param {Function} [predicate=_.identity] The function invoked per iteration. + * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`. + * @returns {boolean} Returns `true` if any element passes the predicate check, + * else `false`. + * @example + * + * _.some([null, 0, 'yes', false], Boolean); + * // => true + * + * var users = [ + * { 'user': 'barney', 'active': true }, + * { 'user': 'fred', 'active': false } + * ]; + * + * // The `_.matches` iteratee shorthand. + * _.some(users, { 'user': 'barney', 'active': false }); + * // => false + * + * // The `_.matchesProperty` iteratee shorthand. + * _.some(users, ['active', false]); + * // => true + * + * // The `_.property` iteratee shorthand. + * _.some(users, 'active'); + * // => true + */ + function some(collection, predicate, guard) { + var func = isArray(collection) ? arraySome : baseSome; + if (guard && isIterateeCall(collection, predicate, guard)) { + predicate = undefined; + } + return func(collection, getIteratee(predicate, 3)); + } + + /** + * Creates an array of elements, sorted in ascending order by the results of + * running each element in a collection thru each iteratee. This method + * performs a stable sort, that is, it preserves the original sort order of + * equal elements. The iteratees are invoked with one argument: (value). + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Collection + * @param {Array|Object} collection The collection to iterate over. + * @param {...(Function|Function[])} [iteratees=[_.identity]] + * The iteratees to sort by. + * @returns {Array} Returns the new sorted array. + * @example + * + * var users = [ + * { 'user': 'fred', 'age': 48 }, + * { 'user': 'barney', 'age': 36 }, + * { 'user': 'fred', 'age': 40 }, + * { 'user': 'barney', 'age': 34 } + * ]; + * + * _.sortBy(users, [function(o) { return o.user; }]); + * // => objects for [['barney', 36], ['barney', 34], ['fred', 48], ['fred', 40]] + * + * _.sortBy(users, ['user', 'age']); + * // => objects for [['barney', 34], ['barney', 36], ['fred', 40], ['fred', 48]] + */ + var sortBy = baseRest(function(collection, iteratees) { + if (collection == null) { + return []; + } + var length = iteratees.length; + if (length > 1 && isIterateeCall(collection, iteratees[0], iteratees[1])) { + iteratees = []; + } else if (length > 2 && isIterateeCall(iteratees[0], iteratees[1], iteratees[2])) { + iteratees = [iteratees[0]]; + } + return baseOrderBy(collection, baseFlatten(iteratees, 1), []); + }); + + /*------------------------------------------------------------------------*/ + + /** + * Gets the timestamp of the number of milliseconds that have elapsed since + * the Unix epoch (1 January 1970 00:00:00 UTC). + * + * @static + * @memberOf _ + * @since 2.4.0 + * @category Date + * @returns {number} Returns the timestamp. + * @example + * + * _.defer(function(stamp) { + * console.log(_.now() - stamp); + * }, _.now()); + * // => Logs the number of milliseconds it took for the deferred invocation. + */ + var now = ctxNow || function() { + return root.Date.now(); + }; + + /*------------------------------------------------------------------------*/ + + /** + * The opposite of `_.before`; this method creates a function that invokes + * `func` once it's called `n` or more times. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Function + * @param {number} n The number of calls before `func` is invoked. + * @param {Function} func The function to restrict. + * @returns {Function} Returns the new restricted function. + * @example + * + * var saves = ['profile', 'settings']; + * + * var done = _.after(saves.length, function() { + * console.log('done saving!'); + * }); + * + * _.forEach(saves, function(type) { + * asyncSave({ 'type': type, 'complete': done }); + * }); + * // => Logs 'done saving!' after the two async saves have completed. + */ + function after(n, func) { + if (typeof func != 'function') { + throw new TypeError(FUNC_ERROR_TEXT); + } + n = toInteger(n); + return function() { + if (--n < 1) { + return func.apply(this, arguments); + } + }; + } + + /** + * Creates a function that invokes `func`, with up to `n` arguments, + * ignoring any additional arguments. + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category Function + * @param {Function} func The function to cap arguments for. + * @param {number} [n=func.length] The arity cap. + * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`. + * @returns {Function} Returns the new capped function. + * @example + * + * _.map(['6', '8', '10'], _.ary(parseInt, 1)); + * // => [6, 8, 10] + */ + function ary(func, n, guard) { + n = guard ? undefined : n; + n = (func && n == null) ? func.length : n; + return createWrap(func, WRAP_ARY_FLAG, undefined, undefined, undefined, undefined, n); + } + + /** + * Creates a function that invokes `func`, with the `this` binding and arguments + * of the created function, while it's called less than `n` times. Subsequent + * calls to the created function return the result of the last `func` invocation. + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category Function + * @param {number} n The number of calls at which `func` is no longer invoked. + * @param {Function} func The function to restrict. + * @returns {Function} Returns the new restricted function. + * @example + * + * jQuery(element).on('click', _.before(5, addContactToList)); + * // => Allows adding up to 4 contacts to the list. + */ + function before(n, func) { + var result; + if (typeof func != 'function') { + throw new TypeError(FUNC_ERROR_TEXT); + } + n = toInteger(n); + return function() { + if (--n > 0) { + result = func.apply(this, arguments); + } + if (n <= 1) { + func = undefined; + } + return result; + }; + } + + /** + * Creates a function that invokes `func` with the `this` binding of `thisArg` + * and `partials` prepended to the arguments it receives. + * + * The `_.bind.placeholder` value, which defaults to `_` in monolithic builds, + * may be used as a placeholder for partially applied arguments. + * + * **Note:** Unlike native `Function#bind`, this method doesn't set the "length" + * property of bound functions. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Function + * @param {Function} func The function to bind. + * @param {*} thisArg The `this` binding of `func`. + * @param {...*} [partials] The arguments to be partially applied. + * @returns {Function} Returns the new bound function. + * @example + * + * function greet(greeting, punctuation) { + * return greeting + ' ' + this.user + punctuation; + * } + * + * var object = { 'user': 'fred' }; + * + * var bound = _.bind(greet, object, 'hi'); + * bound('!'); + * // => 'hi fred!' + * + * // Bound with placeholders. + * var bound = _.bind(greet, object, _, '!'); + * bound('hi'); + * // => 'hi fred!' + */ + var bind = baseRest(function(func, thisArg, partials) { + var bitmask = WRAP_BIND_FLAG; + if (partials.length) { + var holders = replaceHolders(partials, getHolder(bind)); + bitmask |= WRAP_PARTIAL_FLAG; + } + return createWrap(func, bitmask, thisArg, partials, holders); + }); + + /** + * Creates a function that invokes the method at `object[key]` with `partials` + * prepended to the arguments it receives. + * + * This method differs from `_.bind` by allowing bound functions to reference + * methods that may be redefined or don't yet exist. See + * [Peter Michaux's article](http://peter.michaux.ca/articles/lazy-function-definition-pattern) + * for more details. + * + * The `_.bindKey.placeholder` value, which defaults to `_` in monolithic + * builds, may be used as a placeholder for partially applied arguments. + * + * @static + * @memberOf _ + * @since 0.10.0 + * @category Function + * @param {Object} object The object to invoke the method on. + * @param {string} key The key of the method. + * @param {...*} [partials] The arguments to be partially applied. + * @returns {Function} Returns the new bound function. + * @example + * + * var object = { + * 'user': 'fred', + * 'greet': function(greeting, punctuation) { + * return greeting + ' ' + this.user + punctuation; + * } + * }; + * + * var bound = _.bindKey(object, 'greet', 'hi'); + * bound('!'); + * // => 'hi fred!' + * + * object.greet = function(greeting, punctuation) { + * return greeting + 'ya ' + this.user + punctuation; + * }; + * + * bound('!'); + * // => 'hiya fred!' + * + * // Bound with placeholders. + * var bound = _.bindKey(object, 'greet', _, '!'); + * bound('hi'); + * // => 'hiya fred!' + */ + var bindKey = baseRest(function(object, key, partials) { + var bitmask = WRAP_BIND_FLAG | WRAP_BIND_KEY_FLAG; + if (partials.length) { + var holders = replaceHolders(partials, getHolder(bindKey)); + bitmask |= WRAP_PARTIAL_FLAG; + } + return createWrap(key, bitmask, object, partials, holders); + }); + + /** + * Creates a function that accepts arguments of `func` and either invokes + * `func` returning its result, if at least `arity` number of arguments have + * been provided, or returns a function that accepts the remaining `func` + * arguments, and so on. The arity of `func` may be specified if `func.length` + * is not sufficient. + * + * The `_.curry.placeholder` value, which defaults to `_` in monolithic builds, + * may be used as a placeholder for provided arguments. + * + * **Note:** This method doesn't set the "length" property of curried functions. + * + * @static + * @memberOf _ + * @since 2.0.0 + * @category Function + * @param {Function} func The function to curry. + * @param {number} [arity=func.length] The arity of `func`. + * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`. + * @returns {Function} Returns the new curried function. + * @example + * + * var abc = function(a, b, c) { + * return [a, b, c]; + * }; + * + * var curried = _.curry(abc); + * + * curried(1)(2)(3); + * // => [1, 2, 3] + * + * curried(1, 2)(3); + * // => [1, 2, 3] + * + * curried(1, 2, 3); + * // => [1, 2, 3] + * + * // Curried with placeholders. + * curried(1)(_, 3)(2); + * // => [1, 2, 3] + */ + function curry(func, arity, guard) { + arity = guard ? undefined : arity; + var result = createWrap(func, WRAP_CURRY_FLAG, undefined, undefined, undefined, undefined, undefined, arity); + result.placeholder = curry.placeholder; + return result; + } + + /** + * This method is like `_.curry` except that arguments are applied to `func` + * in the manner of `_.partialRight` instead of `_.partial`. + * + * The `_.curryRight.placeholder` value, which defaults to `_` in monolithic + * builds, may be used as a placeholder for provided arguments. + * + * **Note:** This method doesn't set the "length" property of curried functions. + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category Function + * @param {Function} func The function to curry. + * @param {number} [arity=func.length] The arity of `func`. + * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`. + * @returns {Function} Returns the new curried function. + * @example + * + * var abc = function(a, b, c) { + * return [a, b, c]; + * }; + * + * var curried = _.curryRight(abc); + * + * curried(3)(2)(1); + * // => [1, 2, 3] + * + * curried(2, 3)(1); + * // => [1, 2, 3] + * + * curried(1, 2, 3); + * // => [1, 2, 3] + * + * // Curried with placeholders. + * curried(3)(1, _)(2); + * // => [1, 2, 3] + */ + function curryRight(func, arity, guard) { + arity = guard ? undefined : arity; + var result = createWrap(func, WRAP_CURRY_RIGHT_FLAG, undefined, undefined, undefined, undefined, undefined, arity); + result.placeholder = curryRight.placeholder; + return result; + } + + /** + * Creates a debounced function that delays invoking `func` until after `wait` + * milliseconds have elapsed since the last time the debounced function was + * invoked. The debounced function comes with a `cancel` method to cancel + * delayed `func` invocations and a `flush` method to immediately invoke them. + * Provide `options` to indicate whether `func` should be invoked on the + * leading and/or trailing edge of the `wait` timeout. The `func` is invoked + * with the last arguments provided to the debounced function. Subsequent + * calls to the debounced function return the result of the last `func` + * invocation. + * + * **Note:** If `leading` and `trailing` options are `true`, `func` is + * invoked on the trailing edge of the timeout only if the debounced function + * is invoked more than once during the `wait` timeout. + * + * If `wait` is `0` and `leading` is `false`, `func` invocation is deferred + * until to the next tick, similar to `setTimeout` with a timeout of `0`. + * + * See [David Corbacho's article](https://css-tricks.com/debouncing-throttling-explained-examples/) + * for details over the differences between `_.debounce` and `_.throttle`. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Function + * @param {Function} func The function to debounce. + * @param {number} [wait=0] The number of milliseconds to delay. + * @param {Object} [options={}] The options object. + * @param {boolean} [options.leading=false] + * Specify invoking on the leading edge of the timeout. + * @param {number} [options.maxWait] + * The maximum time `func` is allowed to be delayed before it's invoked. + * @param {boolean} [options.trailing=true] + * Specify invoking on the trailing edge of the timeout. + * @returns {Function} Returns the new debounced function. + * @example + * + * // Avoid costly calculations while the window size is in flux. + * jQuery(window).on('resize', _.debounce(calculateLayout, 150)); + * + * // Invoke `sendMail` when clicked, debouncing subsequent calls. + * jQuery(element).on('click', _.debounce(sendMail, 300, { + * 'leading': true, + * 'trailing': false + * })); + * + * // Ensure `batchLog` is invoked once after 1 second of debounced calls. + * var debounced = _.debounce(batchLog, 250, { 'maxWait': 1000 }); + * var source = new EventSource('/stream'); + * jQuery(source).on('message', debounced); + * + * // Cancel the trailing debounced invocation. + * jQuery(window).on('popstate', debounced.cancel); + */ + function debounce(func, wait, options) { + var lastArgs, + lastThis, + maxWait, + result, + timerId, + lastCallTime, + lastInvokeTime = 0, + leading = false, + maxing = false, + trailing = true; + + if (typeof func != 'function') { + throw new TypeError(FUNC_ERROR_TEXT); + } + wait = toNumber(wait) || 0; + if (isObject(options)) { + leading = !!options.leading; + maxing = 'maxWait' in options; + maxWait = maxing ? nativeMax(toNumber(options.maxWait) || 0, wait) : maxWait; + trailing = 'trailing' in options ? !!options.trailing : trailing; + } + + function invokeFunc(time) { + var args = lastArgs, + thisArg = lastThis; + + lastArgs = lastThis = undefined; + lastInvokeTime = time; + result = func.apply(thisArg, args); + return result; + } + + function leadingEdge(time) { + // Reset any `maxWait` timer. + lastInvokeTime = time; + // Start the timer for the trailing edge. + timerId = setTimeout(timerExpired, wait); + // Invoke the leading edge. + return leading ? invokeFunc(time) : result; + } + + function remainingWait(time) { + var timeSinceLastCall = time - lastCallTime, + timeSinceLastInvoke = time - lastInvokeTime, + result = wait - timeSinceLastCall; + + return maxing ? nativeMin(result, maxWait - timeSinceLastInvoke) : result; + } + + function shouldInvoke(time) { + var timeSinceLastCall = time - lastCallTime, + timeSinceLastInvoke = time - lastInvokeTime; + + // Either this is the first call, activity has stopped and we're at the + // trailing edge, the system time has gone backwards and we're treating + // it as the trailing edge, or we've hit the `maxWait` limit. + return (lastCallTime === undefined || (timeSinceLastCall >= wait) || + (timeSinceLastCall < 0) || (maxing && timeSinceLastInvoke >= maxWait)); + } + + function timerExpired() { + var time = now(); + if (shouldInvoke(time)) { + return trailingEdge(time); + } + // Restart the timer. + timerId = setTimeout(timerExpired, remainingWait(time)); + } + + function trailingEdge(time) { + timerId = undefined; + + // Only invoke if we have `lastArgs` which means `func` has been + // debounced at least once. + if (trailing && lastArgs) { + return invokeFunc(time); + } + lastArgs = lastThis = undefined; + return result; + } + + function cancel() { + if (timerId !== undefined) { + clearTimeout(timerId); + } + lastInvokeTime = 0; + lastArgs = lastCallTime = lastThis = timerId = undefined; + } + + function flush() { + return timerId === undefined ? result : trailingEdge(now()); + } + + function debounced() { + var time = now(), + isInvoking = shouldInvoke(time); + + lastArgs = arguments; + lastThis = this; + lastCallTime = time; + + if (isInvoking) { + if (timerId === undefined) { + return leadingEdge(lastCallTime); + } + if (maxing) { + // Handle invocations in a tight loop. + timerId = setTimeout(timerExpired, wait); + return invokeFunc(lastCallTime); + } + } + if (timerId === undefined) { + timerId = setTimeout(timerExpired, wait); + } + return result; + } + debounced.cancel = cancel; + debounced.flush = flush; + return debounced; + } + + /** + * Defers invoking the `func` until the current call stack has cleared. Any + * additional arguments are provided to `func` when it's invoked. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Function + * @param {Function} func The function to defer. + * @param {...*} [args] The arguments to invoke `func` with. + * @returns {number} Returns the timer id. + * @example + * + * _.defer(function(text) { + * console.log(text); + * }, 'deferred'); + * // => Logs 'deferred' after one millisecond. + */ + var defer = baseRest(function(func, args) { + return baseDelay(func, 1, args); + }); + + /** + * Invokes `func` after `wait` milliseconds. Any additional arguments are + * provided to `func` when it's invoked. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Function + * @param {Function} func The function to delay. + * @param {number} wait The number of milliseconds to delay invocation. + * @param {...*} [args] The arguments to invoke `func` with. + * @returns {number} Returns the timer id. + * @example + * + * _.delay(function(text) { + * console.log(text); + * }, 1000, 'later'); + * // => Logs 'later' after one second. + */ + var delay = baseRest(function(func, wait, args) { + return baseDelay(func, toNumber(wait) || 0, args); + }); + + /** + * Creates a function that invokes `func` with arguments reversed. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Function + * @param {Function} func The function to flip arguments for. + * @returns {Function} Returns the new flipped function. + * @example + * + * var flipped = _.flip(function() { + * return _.toArray(arguments); + * }); + * + * flipped('a', 'b', 'c', 'd'); + * // => ['d', 'c', 'b', 'a'] + */ + function flip(func) { + return createWrap(func, WRAP_FLIP_FLAG); + } + + /** + * Creates a function that memoizes the result of `func`. If `resolver` is + * provided, it determines the cache key for storing the result based on the + * arguments provided to the memoized function. By default, the first argument + * provided to the memoized function is used as the map cache key. The `func` + * is invoked with the `this` binding of the memoized function. + * + * **Note:** The cache is exposed as the `cache` property on the memoized + * function. Its creation may be customized by replacing the `_.memoize.Cache` + * constructor with one whose instances implement the + * [`Map`](http://ecma-international.org/ecma-262/7.0/#sec-properties-of-the-map-prototype-object) + * method interface of `clear`, `delete`, `get`, `has`, and `set`. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Function + * @param {Function} func The function to have its output memoized. + * @param {Function} [resolver] The function to resolve the cache key. + * @returns {Function} Returns the new memoized function. + * @example + * + * var object = { 'a': 1, 'b': 2 }; + * var other = { 'c': 3, 'd': 4 }; + * + * var values = _.memoize(_.values); + * values(object); + * // => [1, 2] + * + * values(other); + * // => [3, 4] + * + * object.a = 2; + * values(object); + * // => [1, 2] + * + * // Modify the result cache. + * values.cache.set(object, ['a', 'b']); + * values(object); + * // => ['a', 'b'] + * + * // Replace `_.memoize.Cache`. + * _.memoize.Cache = WeakMap; + */ + function memoize(func, resolver) { + if (typeof func != 'function' || (resolver != null && typeof resolver != 'function')) { + throw new TypeError(FUNC_ERROR_TEXT); + } + var memoized = function() { + var args = arguments, + key = resolver ? resolver.apply(this, args) : args[0], + cache = memoized.cache; + + if (cache.has(key)) { + return cache.get(key); + } + var result = func.apply(this, args); + memoized.cache = cache.set(key, result) || cache; + return result; + }; + memoized.cache = new (memoize.Cache || MapCache); + return memoized; + } + + // Expose `MapCache`. + memoize.Cache = MapCache; + + /** + * Creates a function that negates the result of the predicate `func`. The + * `func` predicate is invoked with the `this` binding and arguments of the + * created function. + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category Function + * @param {Function} predicate The predicate to negate. + * @returns {Function} Returns the new negated function. + * @example + * + * function isEven(n) { + * return n % 2 == 0; + * } + * + * _.filter([1, 2, 3, 4, 5, 6], _.negate(isEven)); + * // => [1, 3, 5] + */ + function negate(predicate) { + if (typeof predicate != 'function') { + throw new TypeError(FUNC_ERROR_TEXT); + } + return function() { + var args = arguments; + switch (args.length) { + case 0: return !predicate.call(this); + case 1: return !predicate.call(this, args[0]); + case 2: return !predicate.call(this, args[0], args[1]); + case 3: return !predicate.call(this, args[0], args[1], args[2]); + } + return !predicate.apply(this, args); + }; + } + + /** + * Creates a function that is restricted to invoking `func` once. Repeat calls + * to the function return the value of the first invocation. The `func` is + * invoked with the `this` binding and arguments of the created function. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Function + * @param {Function} func The function to restrict. + * @returns {Function} Returns the new restricted function. + * @example + * + * var initialize = _.once(createApplication); + * initialize(); + * initialize(); + * // => `createApplication` is invoked once + */ + function once(func) { + return before(2, func); + } + + /** + * Creates a function that invokes `func` with its arguments transformed. + * + * @static + * @since 4.0.0 + * @memberOf _ + * @category Function + * @param {Function} func The function to wrap. + * @param {...(Function|Function[])} [transforms=[_.identity]] + * The argument transforms. + * @returns {Function} Returns the new function. + * @example + * + * function doubled(n) { + * return n * 2; + * } + * + * function square(n) { + * return n * n; + * } + * + * var func = _.overArgs(function(x, y) { + * return [x, y]; + * }, [square, doubled]); + * + * func(9, 3); + * // => [81, 6] + * + * func(10, 5); + * // => [100, 10] + */ + var overArgs = castRest(function(func, transforms) { + transforms = (transforms.length == 1 && isArray(transforms[0])) + ? arrayMap(transforms[0], baseUnary(getIteratee())) + : arrayMap(baseFlatten(transforms, 1), baseUnary(getIteratee())); + + var funcsLength = transforms.length; + return baseRest(function(args) { + var index = -1, + length = nativeMin(args.length, funcsLength); + + while (++index < length) { + args[index] = transforms[index].call(this, args[index]); + } + return apply(func, this, args); + }); + }); + + /** + * Creates a function that invokes `func` with `partials` prepended to the + * arguments it receives. This method is like `_.bind` except it does **not** + * alter the `this` binding. + * + * The `_.partial.placeholder` value, which defaults to `_` in monolithic + * builds, may be used as a placeholder for partially applied arguments. + * + * **Note:** This method doesn't set the "length" property of partially + * applied functions. + * + * @static + * @memberOf _ + * @since 0.2.0 + * @category Function + * @param {Function} func The function to partially apply arguments to. + * @param {...*} [partials] The arguments to be partially applied. + * @returns {Function} Returns the new partially applied function. + * @example + * + * function greet(greeting, name) { + * return greeting + ' ' + name; + * } + * + * var sayHelloTo = _.partial(greet, 'hello'); + * sayHelloTo('fred'); + * // => 'hello fred' + * + * // Partially applied with placeholders. + * var greetFred = _.partial(greet, _, 'fred'); + * greetFred('hi'); + * // => 'hi fred' + */ + var partial = baseRest(function(func, partials) { + var holders = replaceHolders(partials, getHolder(partial)); + return createWrap(func, WRAP_PARTIAL_FLAG, undefined, partials, holders); + }); + + /** + * This method is like `_.partial` except that partially applied arguments + * are appended to the arguments it receives. + * + * The `_.partialRight.placeholder` value, which defaults to `_` in monolithic + * builds, may be used as a placeholder for partially applied arguments. + * + * **Note:** This method doesn't set the "length" property of partially + * applied functions. + * + * @static + * @memberOf _ + * @since 1.0.0 + * @category Function + * @param {Function} func The function to partially apply arguments to. + * @param {...*} [partials] The arguments to be partially applied. + * @returns {Function} Returns the new partially applied function. + * @example + * + * function greet(greeting, name) { + * return greeting + ' ' + name; + * } + * + * var greetFred = _.partialRight(greet, 'fred'); + * greetFred('hi'); + * // => 'hi fred' + * + * // Partially applied with placeholders. + * var sayHelloTo = _.partialRight(greet, 'hello', _); + * sayHelloTo('fred'); + * // => 'hello fred' + */ + var partialRight = baseRest(function(func, partials) { + var holders = replaceHolders(partials, getHolder(partialRight)); + return createWrap(func, WRAP_PARTIAL_RIGHT_FLAG, undefined, partials, holders); + }); + + /** + * Creates a function that invokes `func` with arguments arranged according + * to the specified `indexes` where the argument value at the first index is + * provided as the first argument, the argument value at the second index is + * provided as the second argument, and so on. + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category Function + * @param {Function} func The function to rearrange arguments for. + * @param {...(number|number[])} indexes The arranged argument indexes. + * @returns {Function} Returns the new function. + * @example + * + * var rearged = _.rearg(function(a, b, c) { + * return [a, b, c]; + * }, [2, 0, 1]); + * + * rearged('b', 'c', 'a') + * // => ['a', 'b', 'c'] + */ + var rearg = flatRest(function(func, indexes) { + return createWrap(func, WRAP_REARG_FLAG, undefined, undefined, undefined, indexes); + }); + + /** + * Creates a function that invokes `func` with the `this` binding of the + * created function and arguments from `start` and beyond provided as + * an array. + * + * **Note:** This method is based on the + * [rest parameter](https://mdn.io/rest_parameters). + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Function + * @param {Function} func The function to apply a rest parameter to. + * @param {number} [start=func.length-1] The start position of the rest parameter. + * @returns {Function} Returns the new function. + * @example + * + * var say = _.rest(function(what, names) { + * return what + ' ' + _.initial(names).join(', ') + + * (_.size(names) > 1 ? ', & ' : '') + _.last(names); + * }); + * + * say('hello', 'fred', 'barney', 'pebbles'); + * // => 'hello fred, barney, & pebbles' + */ + function rest(func, start) { + if (typeof func != 'function') { + throw new TypeError(FUNC_ERROR_TEXT); + } + start = start === undefined ? start : toInteger(start); + return baseRest(func, start); + } + + /** + * Creates a function that invokes `func` with the `this` binding of the + * create function and an array of arguments much like + * [`Function#apply`](http://www.ecma-international.org/ecma-262/7.0/#sec-function.prototype.apply). + * + * **Note:** This method is based on the + * [spread operator](https://mdn.io/spread_operator). + * + * @static + * @memberOf _ + * @since 3.2.0 + * @category Function + * @param {Function} func The function to spread arguments over. + * @param {number} [start=0] The start position of the spread. + * @returns {Function} Returns the new function. + * @example + * + * var say = _.spread(function(who, what) { + * return who + ' says ' + what; + * }); + * + * say(['fred', 'hello']); + * // => 'fred says hello' + * + * var numbers = Promise.all([ + * Promise.resolve(40), + * Promise.resolve(36) + * ]); + * + * numbers.then(_.spread(function(x, y) { + * return x + y; + * })); + * // => a Promise of 76 + */ + function spread(func, start) { + if (typeof func != 'function') { + throw new TypeError(FUNC_ERROR_TEXT); + } + start = start == null ? 0 : nativeMax(toInteger(start), 0); + return baseRest(function(args) { + var array = args[start], + otherArgs = castSlice(args, 0, start); + + if (array) { + arrayPush(otherArgs, array); + } + return apply(func, this, otherArgs); + }); + } + + /** + * Creates a throttled function that only invokes `func` at most once per + * every `wait` milliseconds. The throttled function comes with a `cancel` + * method to cancel delayed `func` invocations and a `flush` method to + * immediately invoke them. Provide `options` to indicate whether `func` + * should be invoked on the leading and/or trailing edge of the `wait` + * timeout. The `func` is invoked with the last arguments provided to the + * throttled function. Subsequent calls to the throttled function return the + * result of the last `func` invocation. + * + * **Note:** If `leading` and `trailing` options are `true`, `func` is + * invoked on the trailing edge of the timeout only if the throttled function + * is invoked more than once during the `wait` timeout. + * + * If `wait` is `0` and `leading` is `false`, `func` invocation is deferred + * until to the next tick, similar to `setTimeout` with a timeout of `0`. + * + * See [David Corbacho's article](https://css-tricks.com/debouncing-throttling-explained-examples/) + * for details over the differences between `_.throttle` and `_.debounce`. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Function + * @param {Function} func The function to throttle. + * @param {number} [wait=0] The number of milliseconds to throttle invocations to. + * @param {Object} [options={}] The options object. + * @param {boolean} [options.leading=true] + * Specify invoking on the leading edge of the timeout. + * @param {boolean} [options.trailing=true] + * Specify invoking on the trailing edge of the timeout. + * @returns {Function} Returns the new throttled function. + * @example + * + * // Avoid excessively updating the position while scrolling. + * jQuery(window).on('scroll', _.throttle(updatePosition, 100)); + * + * // Invoke `renewToken` when the click event is fired, but not more than once every 5 minutes. + * var throttled = _.throttle(renewToken, 300000, { 'trailing': false }); + * jQuery(element).on('click', throttled); + * + * // Cancel the trailing throttled invocation. + * jQuery(window).on('popstate', throttled.cancel); + */ + function throttle(func, wait, options) { + var leading = true, + trailing = true; + + if (typeof func != 'function') { + throw new TypeError(FUNC_ERROR_TEXT); + } + if (isObject(options)) { + leading = 'leading' in options ? !!options.leading : leading; + trailing = 'trailing' in options ? !!options.trailing : trailing; + } + return debounce(func, wait, { + 'leading': leading, + 'maxWait': wait, + 'trailing': trailing + }); + } + + /** + * Creates a function that accepts up to one argument, ignoring any + * additional arguments. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Function + * @param {Function} func The function to cap arguments for. + * @returns {Function} Returns the new capped function. + * @example + * + * _.map(['6', '8', '10'], _.unary(parseInt)); + * // => [6, 8, 10] + */ + function unary(func) { + return ary(func, 1); + } + + /** + * Creates a function that provides `value` to `wrapper` as its first + * argument. Any additional arguments provided to the function are appended + * to those provided to the `wrapper`. The wrapper is invoked with the `this` + * binding of the created function. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Function + * @param {*} value The value to wrap. + * @param {Function} [wrapper=identity] The wrapper function. + * @returns {Function} Returns the new function. + * @example + * + * var p = _.wrap(_.escape, function(func, text) { + * return '

' + func(text) + '

'; + * }); + * + * p('fred, barney, & pebbles'); + * // => '

fred, barney, & pebbles

' + */ + function wrap(value, wrapper) { + return partial(castFunction(wrapper), value); + } + + /*------------------------------------------------------------------------*/ + + /** + * Casts `value` as an array if it's not one. + * + * @static + * @memberOf _ + * @since 4.4.0 + * @category Lang + * @param {*} value The value to inspect. + * @returns {Array} Returns the cast array. + * @example + * + * _.castArray(1); + * // => [1] + * + * _.castArray({ 'a': 1 }); + * // => [{ 'a': 1 }] + * + * _.castArray('abc'); + * // => ['abc'] + * + * _.castArray(null); + * // => [null] + * + * _.castArray(undefined); + * // => [undefined] + * + * _.castArray(); + * // => [] + * + * var array = [1, 2, 3]; + * console.log(_.castArray(array) === array); + * // => true + */ + function castArray() { + if (!arguments.length) { + return []; + } + var value = arguments[0]; + return isArray(value) ? value : [value]; + } + + /** + * Creates a shallow clone of `value`. + * + * **Note:** This method is loosely based on the + * [structured clone algorithm](https://mdn.io/Structured_clone_algorithm) + * and supports cloning arrays, array buffers, booleans, date objects, maps, + * numbers, `Object` objects, regexes, sets, strings, symbols, and typed + * arrays. The own enumerable properties of `arguments` objects are cloned + * as plain objects. An empty object is returned for uncloneable values such + * as error objects, functions, DOM nodes, and WeakMaps. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Lang + * @param {*} value The value to clone. + * @returns {*} Returns the cloned value. + * @see _.cloneDeep + * @example + * + * var objects = [{ 'a': 1 }, { 'b': 2 }]; + * + * var shallow = _.clone(objects); + * console.log(shallow[0] === objects[0]); + * // => true + */ + function clone(value) { + return baseClone(value, CLONE_SYMBOLS_FLAG); + } + + /** + * This method is like `_.clone` except that it accepts `customizer` which + * is invoked to produce the cloned value. If `customizer` returns `undefined`, + * cloning is handled by the method instead. The `customizer` is invoked with + * up to four arguments; (value [, index|key, object, stack]). + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to clone. + * @param {Function} [customizer] The function to customize cloning. + * @returns {*} Returns the cloned value. + * @see _.cloneDeepWith + * @example + * + * function customizer(value) { + * if (_.isElement(value)) { + * return value.cloneNode(false); + * } + * } + * + * var el = _.cloneWith(document.body, customizer); + * + * console.log(el === document.body); + * // => false + * console.log(el.nodeName); + * // => 'BODY' + * console.log(el.childNodes.length); + * // => 0 + */ + function cloneWith(value, customizer) { + customizer = typeof customizer == 'function' ? customizer : undefined; + return baseClone(value, CLONE_SYMBOLS_FLAG, customizer); + } + + /** + * This method is like `_.clone` except that it recursively clones `value`. + * + * @static + * @memberOf _ + * @since 1.0.0 + * @category Lang + * @param {*} value The value to recursively clone. + * @returns {*} Returns the deep cloned value. + * @see _.clone + * @example + * + * var objects = [{ 'a': 1 }, { 'b': 2 }]; + * + * var deep = _.cloneDeep(objects); + * console.log(deep[0] === objects[0]); + * // => false + */ + function cloneDeep(value) { + return baseClone(value, CLONE_DEEP_FLAG | CLONE_SYMBOLS_FLAG); + } + + /** + * This method is like `_.cloneWith` except that it recursively clones `value`. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to recursively clone. + * @param {Function} [customizer] The function to customize cloning. + * @returns {*} Returns the deep cloned value. + * @see _.cloneWith + * @example + * + * function customizer(value) { + * if (_.isElement(value)) { + * return value.cloneNode(true); + * } + * } + * + * var el = _.cloneDeepWith(document.body, customizer); + * + * console.log(el === document.body); + * // => false + * console.log(el.nodeName); + * // => 'BODY' + * console.log(el.childNodes.length); + * // => 20 + */ + function cloneDeepWith(value, customizer) { + customizer = typeof customizer == 'function' ? customizer : undefined; + return baseClone(value, CLONE_DEEP_FLAG | CLONE_SYMBOLS_FLAG, customizer); + } + + /** + * Checks if `object` conforms to `source` by invoking the predicate + * properties of `source` with the corresponding property values of `object`. + * + * **Note:** This method is equivalent to `_.conforms` when `source` is + * partially applied. + * + * @static + * @memberOf _ + * @since 4.14.0 + * @category Lang + * @param {Object} object The object to inspect. + * @param {Object} source The object of property predicates to conform to. + * @returns {boolean} Returns `true` if `object` conforms, else `false`. + * @example + * + * var object = { 'a': 1, 'b': 2 }; + * + * _.conformsTo(object, { 'b': function(n) { return n > 1; } }); + * // => true + * + * _.conformsTo(object, { 'b': function(n) { return n > 2; } }); + * // => false + */ + function conformsTo(object, source) { + return source == null || baseConformsTo(object, source, keys(source)); + } + + /** + * Performs a + * [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero) + * comparison between two values to determine if they are equivalent. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to compare. + * @param {*} other The other value to compare. + * @returns {boolean} Returns `true` if the values are equivalent, else `false`. + * @example + * + * var object = { 'a': 1 }; + * var other = { 'a': 1 }; + * + * _.eq(object, object); + * // => true + * + * _.eq(object, other); + * // => false + * + * _.eq('a', 'a'); + * // => true + * + * _.eq('a', Object('a')); + * // => false + * + * _.eq(NaN, NaN); + * // => true + */ + function eq(value, other) { + return value === other || (value !== value && other !== other); + } + + /** + * Checks if `value` is greater than `other`. + * + * @static + * @memberOf _ + * @since 3.9.0 + * @category Lang + * @param {*} value The value to compare. + * @param {*} other The other value to compare. + * @returns {boolean} Returns `true` if `value` is greater than `other`, + * else `false`. + * @see _.lt + * @example + * + * _.gt(3, 1); + * // => true + * + * _.gt(3, 3); + * // => false + * + * _.gt(1, 3); + * // => false + */ + var gt = createRelationalOperation(baseGt); + + /** + * Checks if `value` is greater than or equal to `other`. + * + * @static + * @memberOf _ + * @since 3.9.0 + * @category Lang + * @param {*} value The value to compare. + * @param {*} other The other value to compare. + * @returns {boolean} Returns `true` if `value` is greater than or equal to + * `other`, else `false`. + * @see _.lte + * @example + * + * _.gte(3, 1); + * // => true + * + * _.gte(3, 3); + * // => true + * + * _.gte(1, 3); + * // => false + */ + var gte = createRelationalOperation(function(value, other) { + return value >= other; + }); + + /** + * Checks if `value` is likely an `arguments` object. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is an `arguments` object, + * else `false`. + * @example + * + * _.isArguments(function() { return arguments; }()); + * // => true + * + * _.isArguments([1, 2, 3]); + * // => false + */ + var isArguments = baseIsArguments(function() { return arguments; }()) ? baseIsArguments : function(value) { + return isObjectLike(value) && hasOwnProperty.call(value, 'callee') && + !propertyIsEnumerable.call(value, 'callee'); + }; + + /** + * Checks if `value` is classified as an `Array` object. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is an array, else `false`. + * @example + * + * _.isArray([1, 2, 3]); + * // => true + * + * _.isArray(document.body.children); + * // => false + * + * _.isArray('abc'); + * // => false + * + * _.isArray(_.noop); + * // => false + */ + var isArray = Array.isArray; + + /** + * Checks if `value` is classified as an `ArrayBuffer` object. + * + * @static + * @memberOf _ + * @since 4.3.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is an array buffer, else `false`. + * @example + * + * _.isArrayBuffer(new ArrayBuffer(2)); + * // => true + * + * _.isArrayBuffer(new Array(2)); + * // => false + */ + var isArrayBuffer = nodeIsArrayBuffer ? baseUnary(nodeIsArrayBuffer) : baseIsArrayBuffer; + + /** + * Checks if `value` is array-like. A value is considered array-like if it's + * not a function and has a `value.length` that's an integer greater than or + * equal to `0` and less than or equal to `Number.MAX_SAFE_INTEGER`. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is array-like, else `false`. + * @example + * + * _.isArrayLike([1, 2, 3]); + * // => true + * + * _.isArrayLike(document.body.children); + * // => true + * + * _.isArrayLike('abc'); + * // => true + * + * _.isArrayLike(_.noop); + * // => false + */ + function isArrayLike(value) { + return value != null && isLength(value.length) && !isFunction(value); + } + + /** + * This method is like `_.isArrayLike` except that it also checks if `value` + * is an object. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is an array-like object, + * else `false`. + * @example + * + * _.isArrayLikeObject([1, 2, 3]); + * // => true + * + * _.isArrayLikeObject(document.body.children); + * // => true + * + * _.isArrayLikeObject('abc'); + * // => false + * + * _.isArrayLikeObject(_.noop); + * // => false + */ + function isArrayLikeObject(value) { + return isObjectLike(value) && isArrayLike(value); + } + + /** + * Checks if `value` is classified as a boolean primitive or object. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a boolean, else `false`. + * @example + * + * _.isBoolean(false); + * // => true + * + * _.isBoolean(null); + * // => false + */ + function isBoolean(value) { + return value === true || value === false || + (isObjectLike(value) && baseGetTag(value) == boolTag); + } + + /** + * Checks if `value` is a buffer. + * + * @static + * @memberOf _ + * @since 4.3.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a buffer, else `false`. + * @example + * + * _.isBuffer(new Buffer(2)); + * // => true + * + * _.isBuffer(new Uint8Array(2)); + * // => false + */ + var isBuffer = nativeIsBuffer || stubFalse; + + /** + * Checks if `value` is classified as a `Date` object. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a date object, else `false`. + * @example + * + * _.isDate(new Date); + * // => true + * + * _.isDate('Mon April 23 2012'); + * // => false + */ + var isDate = nodeIsDate ? baseUnary(nodeIsDate) : baseIsDate; + + /** + * Checks if `value` is likely a DOM element. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a DOM element, else `false`. + * @example + * + * _.isElement(document.body); + * // => true + * + * _.isElement(''); + * // => false + */ + function isElement(value) { + return isObjectLike(value) && value.nodeType === 1 && !isPlainObject(value); + } + + /** + * Checks if `value` is an empty object, collection, map, or set. + * + * Objects are considered empty if they have no own enumerable string keyed + * properties. + * + * Array-like values such as `arguments` objects, arrays, buffers, strings, or + * jQuery-like collections are considered empty if they have a `length` of `0`. + * Similarly, maps and sets are considered empty if they have a `size` of `0`. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is empty, else `false`. + * @example + * + * _.isEmpty(null); + * // => true + * + * _.isEmpty(true); + * // => true + * + * _.isEmpty(1); + * // => true + * + * _.isEmpty([1, 2, 3]); + * // => false + * + * _.isEmpty({ 'a': 1 }); + * // => false + */ + function isEmpty(value) { + if (value == null) { + return true; + } + if (isArrayLike(value) && + (isArray(value) || typeof value == 'string' || typeof value.splice == 'function' || + isBuffer(value) || isTypedArray(value) || isArguments(value))) { + return !value.length; + } + var tag = getTag(value); + if (tag == mapTag || tag == setTag) { + return !value.size; + } + if (isPrototype(value)) { + return !baseKeys(value).length; + } + for (var key in value) { + if (hasOwnProperty.call(value, key)) { + return false; + } + } + return true; + } + + /** + * Performs a deep comparison between two values to determine if they are + * equivalent. + * + * **Note:** This method supports comparing arrays, array buffers, booleans, + * date objects, error objects, maps, numbers, `Object` objects, regexes, + * sets, strings, symbols, and typed arrays. `Object` objects are compared + * by their own, not inherited, enumerable properties. Functions and DOM + * nodes are compared by strict equality, i.e. `===`. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Lang + * @param {*} value The value to compare. + * @param {*} other The other value to compare. + * @returns {boolean} Returns `true` if the values are equivalent, else `false`. + * @example + * + * var object = { 'a': 1 }; + * var other = { 'a': 1 }; + * + * _.isEqual(object, other); + * // => true + * + * object === other; + * // => false + */ + function isEqual(value, other) { + return baseIsEqual(value, other); + } + + /** + * This method is like `_.isEqual` except that it accepts `customizer` which + * is invoked to compare values. If `customizer` returns `undefined`, comparisons + * are handled by the method instead. The `customizer` is invoked with up to + * six arguments: (objValue, othValue [, index|key, object, other, stack]). + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to compare. + * @param {*} other The other value to compare. + * @param {Function} [customizer] The function to customize comparisons. + * @returns {boolean} Returns `true` if the values are equivalent, else `false`. + * @example + * + * function isGreeting(value) { + * return /^h(?:i|ello)$/.test(value); + * } + * + * function customizer(objValue, othValue) { + * if (isGreeting(objValue) && isGreeting(othValue)) { + * return true; + * } + * } + * + * var array = ['hello', 'goodbye']; + * var other = ['hi', 'goodbye']; + * + * _.isEqualWith(array, other, customizer); + * // => true + */ + function isEqualWith(value, other, customizer) { + customizer = typeof customizer == 'function' ? customizer : undefined; + var result = customizer ? customizer(value, other) : undefined; + return result === undefined ? baseIsEqual(value, other, undefined, customizer) : !!result; + } + + /** + * Checks if `value` is an `Error`, `EvalError`, `RangeError`, `ReferenceError`, + * `SyntaxError`, `TypeError`, or `URIError` object. + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is an error object, else `false`. + * @example + * + * _.isError(new Error); + * // => true + * + * _.isError(Error); + * // => false + */ + function isError(value) { + if (!isObjectLike(value)) { + return false; + } + var tag = baseGetTag(value); + return tag == errorTag || tag == domExcTag || + (typeof value.message == 'string' && typeof value.name == 'string' && !isPlainObject(value)); + } + + /** + * Checks if `value` is a finite primitive number. + * + * **Note:** This method is based on + * [`Number.isFinite`](https://mdn.io/Number/isFinite). + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a finite number, else `false`. + * @example + * + * _.isFinite(3); + * // => true + * + * _.isFinite(Number.MIN_VALUE); + * // => true + * + * _.isFinite(Infinity); + * // => false + * + * _.isFinite('3'); + * // => false + */ + function isFinite(value) { + return typeof value == 'number' && nativeIsFinite(value); + } + + /** + * Checks if `value` is classified as a `Function` object. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a function, else `false`. + * @example + * + * _.isFunction(_); + * // => true + * + * _.isFunction(/abc/); + * // => false + */ + function isFunction(value) { + if (!isObject(value)) { + return false; + } + // The use of `Object#toString` avoids issues with the `typeof` operator + // in Safari 9 which returns 'object' for typed arrays and other constructors. + var tag = baseGetTag(value); + return tag == funcTag || tag == genTag || tag == asyncTag || tag == proxyTag; + } + + /** + * Checks if `value` is an integer. + * + * **Note:** This method is based on + * [`Number.isInteger`](https://mdn.io/Number/isInteger). + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is an integer, else `false`. + * @example + * + * _.isInteger(3); + * // => true + * + * _.isInteger(Number.MIN_VALUE); + * // => false + * + * _.isInteger(Infinity); + * // => false + * + * _.isInteger('3'); + * // => false + */ + function isInteger(value) { + return typeof value == 'number' && value == toInteger(value); + } + + /** + * Checks if `value` is a valid array-like length. + * + * **Note:** This method is loosely based on + * [`ToLength`](http://ecma-international.org/ecma-262/7.0/#sec-tolength). + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a valid length, else `false`. + * @example + * + * _.isLength(3); + * // => true + * + * _.isLength(Number.MIN_VALUE); + * // => false + * + * _.isLength(Infinity); + * // => false + * + * _.isLength('3'); + * // => false + */ + function isLength(value) { + return typeof value == 'number' && + value > -1 && value % 1 == 0 && value <= MAX_SAFE_INTEGER; + } + + /** + * Checks if `value` is the + * [language type](http://www.ecma-international.org/ecma-262/7.0/#sec-ecmascript-language-types) + * of `Object`. (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`) + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is an object, else `false`. + * @example + * + * _.isObject({}); + * // => true + * + * _.isObject([1, 2, 3]); + * // => true + * + * _.isObject(_.noop); + * // => true + * + * _.isObject(null); + * // => false + */ + function isObject(value) { + var type = typeof value; + return value != null && (type == 'object' || type == 'function'); + } + + /** + * Checks if `value` is object-like. A value is object-like if it's not `null` + * and has a `typeof` result of "object". + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is object-like, else `false`. + * @example + * + * _.isObjectLike({}); + * // => true + * + * _.isObjectLike([1, 2, 3]); + * // => true + * + * _.isObjectLike(_.noop); + * // => false + * + * _.isObjectLike(null); + * // => false + */ + function isObjectLike(value) { + return value != null && typeof value == 'object'; + } + + /** + * Checks if `value` is classified as a `Map` object. + * + * @static + * @memberOf _ + * @since 4.3.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a map, else `false`. + * @example + * + * _.isMap(new Map); + * // => true + * + * _.isMap(new WeakMap); + * // => false + */ + var isMap = nodeIsMap ? baseUnary(nodeIsMap) : baseIsMap; + + /** + * Performs a partial deep comparison between `object` and `source` to + * determine if `object` contains equivalent property values. + * + * **Note:** This method is equivalent to `_.matches` when `source` is + * partially applied. + * + * Partial comparisons will match empty array and empty object `source` + * values against any array or object value, respectively. See `_.isEqual` + * for a list of supported value comparisons. + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category Lang + * @param {Object} object The object to inspect. + * @param {Object} source The object of property values to match. + * @returns {boolean} Returns `true` if `object` is a match, else `false`. + * @example + * + * var object = { 'a': 1, 'b': 2 }; + * + * _.isMatch(object, { 'b': 2 }); + * // => true + * + * _.isMatch(object, { 'b': 1 }); + * // => false + */ + function isMatch(object, source) { + return object === source || baseIsMatch(object, source, getMatchData(source)); + } + + /** + * This method is like `_.isMatch` except that it accepts `customizer` which + * is invoked to compare values. If `customizer` returns `undefined`, comparisons + * are handled by the method instead. The `customizer` is invoked with five + * arguments: (objValue, srcValue, index|key, object, source). + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {Object} object The object to inspect. + * @param {Object} source The object of property values to match. + * @param {Function} [customizer] The function to customize comparisons. + * @returns {boolean} Returns `true` if `object` is a match, else `false`. + * @example + * + * function isGreeting(value) { + * return /^h(?:i|ello)$/.test(value); + * } + * + * function customizer(objValue, srcValue) { + * if (isGreeting(objValue) && isGreeting(srcValue)) { + * return true; + * } + * } + * + * var object = { 'greeting': 'hello' }; + * var source = { 'greeting': 'hi' }; + * + * _.isMatchWith(object, source, customizer); + * // => true + */ + function isMatchWith(object, source, customizer) { + customizer = typeof customizer == 'function' ? customizer : undefined; + return baseIsMatch(object, source, getMatchData(source), customizer); + } + + /** + * Checks if `value` is `NaN`. + * + * **Note:** This method is based on + * [`Number.isNaN`](https://mdn.io/Number/isNaN) and is not the same as + * global [`isNaN`](https://mdn.io/isNaN) which returns `true` for + * `undefined` and other non-number values. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is `NaN`, else `false`. + * @example + * + * _.isNaN(NaN); + * // => true + * + * _.isNaN(new Number(NaN)); + * // => true + * + * isNaN(undefined); + * // => true + * + * _.isNaN(undefined); + * // => false + */ + function isNaN(value) { + // An `NaN` primitive is the only value that is not equal to itself. + // Perform the `toStringTag` check first to avoid errors with some + // ActiveX objects in IE. + return isNumber(value) && value != +value; + } + + /** + * Checks if `value` is a pristine native function. + * + * **Note:** This method can't reliably detect native functions in the presence + * of the core-js package because core-js circumvents this kind of detection. + * Despite multiple requests, the core-js maintainer has made it clear: any + * attempt to fix the detection will be obstructed. As a result, we're left + * with little choice but to throw an error. Unfortunately, this also affects + * packages, like [babel-polyfill](https://www.npmjs.com/package/babel-polyfill), + * which rely on core-js. + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a native function, + * else `false`. + * @example + * + * _.isNative(Array.prototype.push); + * // => true + * + * _.isNative(_); + * // => false + */ + function isNative(value) { + if (isMaskable(value)) { + throw new Error(CORE_ERROR_TEXT); + } + return baseIsNative(value); + } + + /** + * Checks if `value` is `null`. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is `null`, else `false`. + * @example + * + * _.isNull(null); + * // => true + * + * _.isNull(void 0); + * // => false + */ + function isNull(value) { + return value === null; + } + + /** + * Checks if `value` is `null` or `undefined`. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is nullish, else `false`. + * @example + * + * _.isNil(null); + * // => true + * + * _.isNil(void 0); + * // => true + * + * _.isNil(NaN); + * // => false + */ + function isNil(value) { + return value == null; + } + + /** + * Checks if `value` is classified as a `Number` primitive or object. + * + * **Note:** To exclude `Infinity`, `-Infinity`, and `NaN`, which are + * classified as numbers, use the `_.isFinite` method. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a number, else `false`. + * @example + * + * _.isNumber(3); + * // => true + * + * _.isNumber(Number.MIN_VALUE); + * // => true + * + * _.isNumber(Infinity); + * // => true + * + * _.isNumber('3'); + * // => false + */ + function isNumber(value) { + return typeof value == 'number' || + (isObjectLike(value) && baseGetTag(value) == numberTag); + } + + /** + * Checks if `value` is a plain object, that is, an object created by the + * `Object` constructor or one with a `[[Prototype]]` of `null`. + * + * @static + * @memberOf _ + * @since 0.8.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a plain object, else `false`. + * @example + * + * function Foo() { + * this.a = 1; + * } + * + * _.isPlainObject(new Foo); + * // => false + * + * _.isPlainObject([1, 2, 3]); + * // => false + * + * _.isPlainObject({ 'x': 0, 'y': 0 }); + * // => true + * + * _.isPlainObject(Object.create(null)); + * // => true + */ + function isPlainObject(value) { + if (!isObjectLike(value) || baseGetTag(value) != objectTag) { + return false; + } + var proto = getPrototype(value); + if (proto === null) { + return true; + } + var Ctor = hasOwnProperty.call(proto, 'constructor') && proto.constructor; + return typeof Ctor == 'function' && Ctor instanceof Ctor && + funcToString.call(Ctor) == objectCtorString; + } + + /** + * Checks if `value` is classified as a `RegExp` object. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a regexp, else `false`. + * @example + * + * _.isRegExp(/abc/); + * // => true + * + * _.isRegExp('/abc/'); + * // => false + */ + var isRegExp = nodeIsRegExp ? baseUnary(nodeIsRegExp) : baseIsRegExp; + + /** + * Checks if `value` is a safe integer. An integer is safe if it's an IEEE-754 + * double precision number which isn't the result of a rounded unsafe integer. + * + * **Note:** This method is based on + * [`Number.isSafeInteger`](https://mdn.io/Number/isSafeInteger). + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a safe integer, else `false`. + * @example + * + * _.isSafeInteger(3); + * // => true + * + * _.isSafeInteger(Number.MIN_VALUE); + * // => false + * + * _.isSafeInteger(Infinity); + * // => false + * + * _.isSafeInteger('3'); + * // => false + */ + function isSafeInteger(value) { + return isInteger(value) && value >= -MAX_SAFE_INTEGER && value <= MAX_SAFE_INTEGER; + } + + /** + * Checks if `value` is classified as a `Set` object. + * + * @static + * @memberOf _ + * @since 4.3.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a set, else `false`. + * @example + * + * _.isSet(new Set); + * // => true + * + * _.isSet(new WeakSet); + * // => false + */ + var isSet = nodeIsSet ? baseUnary(nodeIsSet) : baseIsSet; + + /** + * Checks if `value` is classified as a `String` primitive or object. + * + * @static + * @since 0.1.0 + * @memberOf _ + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a string, else `false`. + * @example + * + * _.isString('abc'); + * // => true + * + * _.isString(1); + * // => false + */ + function isString(value) { + return typeof value == 'string' || + (!isArray(value) && isObjectLike(value) && baseGetTag(value) == stringTag); + } + + /** + * Checks if `value` is classified as a `Symbol` primitive or object. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a symbol, else `false`. + * @example + * + * _.isSymbol(Symbol.iterator); + * // => true + * + * _.isSymbol('abc'); + * // => false + */ + function isSymbol(value) { + return typeof value == 'symbol' || + (isObjectLike(value) && baseGetTag(value) == symbolTag); + } + + /** + * Checks if `value` is classified as a typed array. + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a typed array, else `false`. + * @example + * + * _.isTypedArray(new Uint8Array); + * // => true + * + * _.isTypedArray([]); + * // => false + */ + var isTypedArray = nodeIsTypedArray ? baseUnary(nodeIsTypedArray) : baseIsTypedArray; + + /** + * Checks if `value` is `undefined`. + * + * @static + * @since 0.1.0 + * @memberOf _ + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is `undefined`, else `false`. + * @example + * + * _.isUndefined(void 0); + * // => true + * + * _.isUndefined(null); + * // => false + */ + function isUndefined(value) { + return value === undefined; + } + + /** + * Checks if `value` is classified as a `WeakMap` object. + * + * @static + * @memberOf _ + * @since 4.3.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a weak map, else `false`. + * @example + * + * _.isWeakMap(new WeakMap); + * // => true + * + * _.isWeakMap(new Map); + * // => false + */ + function isWeakMap(value) { + return isObjectLike(value) && getTag(value) == weakMapTag; + } + + /** + * Checks if `value` is classified as a `WeakSet` object. + * + * @static + * @memberOf _ + * @since 4.3.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a weak set, else `false`. + * @example + * + * _.isWeakSet(new WeakSet); + * // => true + * + * _.isWeakSet(new Set); + * // => false + */ + function isWeakSet(value) { + return isObjectLike(value) && baseGetTag(value) == weakSetTag; + } + + /** + * Checks if `value` is less than `other`. + * + * @static + * @memberOf _ + * @since 3.9.0 + * @category Lang + * @param {*} value The value to compare. + * @param {*} other The other value to compare. + * @returns {boolean} Returns `true` if `value` is less than `other`, + * else `false`. + * @see _.gt + * @example + * + * _.lt(1, 3); + * // => true + * + * _.lt(3, 3); + * // => false + * + * _.lt(3, 1); + * // => false + */ + var lt = createRelationalOperation(baseLt); + + /** + * Checks if `value` is less than or equal to `other`. + * + * @static + * @memberOf _ + * @since 3.9.0 + * @category Lang + * @param {*} value The value to compare. + * @param {*} other The other value to compare. + * @returns {boolean} Returns `true` if `value` is less than or equal to + * `other`, else `false`. + * @see _.gte + * @example + * + * _.lte(1, 3); + * // => true + * + * _.lte(3, 3); + * // => true + * + * _.lte(3, 1); + * // => false + */ + var lte = createRelationalOperation(function(value, other) { + return value <= other; + }); + + /** + * Converts `value` to an array. + * + * @static + * @since 0.1.0 + * @memberOf _ + * @category Lang + * @param {*} value The value to convert. + * @returns {Array} Returns the converted array. + * @example + * + * _.toArray({ 'a': 1, 'b': 2 }); + * // => [1, 2] + * + * _.toArray('abc'); + * // => ['a', 'b', 'c'] + * + * _.toArray(1); + * // => [] + * + * _.toArray(null); + * // => [] + */ + function toArray(value) { + if (!value) { + return []; + } + if (isArrayLike(value)) { + return isString(value) ? stringToArray(value) : copyArray(value); + } + if (symIterator && value[symIterator]) { + return iteratorToArray(value[symIterator]()); + } + var tag = getTag(value), + func = tag == mapTag ? mapToArray : (tag == setTag ? setToArray : values); + + return func(value); + } + + /** + * Converts `value` to a finite number. + * + * @static + * @memberOf _ + * @since 4.12.0 + * @category Lang + * @param {*} value The value to convert. + * @returns {number} Returns the converted number. + * @example + * + * _.toFinite(3.2); + * // => 3.2 + * + * _.toFinite(Number.MIN_VALUE); + * // => 5e-324 + * + * _.toFinite(Infinity); + * // => 1.7976931348623157e+308 + * + * _.toFinite('3.2'); + * // => 3.2 + */ + function toFinite(value) { + if (!value) { + return value === 0 ? value : 0; + } + value = toNumber(value); + if (value === INFINITY || value === -INFINITY) { + var sign = (value < 0 ? -1 : 1); + return sign * MAX_INTEGER; + } + return value === value ? value : 0; + } + + /** + * Converts `value` to an integer. + * + * **Note:** This method is loosely based on + * [`ToInteger`](http://www.ecma-international.org/ecma-262/7.0/#sec-tointeger). + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to convert. + * @returns {number} Returns the converted integer. + * @example + * + * _.toInteger(3.2); + * // => 3 + * + * _.toInteger(Number.MIN_VALUE); + * // => 0 + * + * _.toInteger(Infinity); + * // => 1.7976931348623157e+308 + * + * _.toInteger('3.2'); + * // => 3 + */ + function toInteger(value) { + var result = toFinite(value), + remainder = result % 1; + + return result === result ? (remainder ? result - remainder : result) : 0; + } + + /** + * Converts `value` to an integer suitable for use as the length of an + * array-like object. + * + * **Note:** This method is based on + * [`ToLength`](http://ecma-international.org/ecma-262/7.0/#sec-tolength). + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to convert. + * @returns {number} Returns the converted integer. + * @example + * + * _.toLength(3.2); + * // => 3 + * + * _.toLength(Number.MIN_VALUE); + * // => 0 + * + * _.toLength(Infinity); + * // => 4294967295 + * + * _.toLength('3.2'); + * // => 3 + */ + function toLength(value) { + return value ? baseClamp(toInteger(value), 0, MAX_ARRAY_LENGTH) : 0; + } + + /** + * Converts `value` to a number. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to process. + * @returns {number} Returns the number. + * @example + * + * _.toNumber(3.2); + * // => 3.2 + * + * _.toNumber(Number.MIN_VALUE); + * // => 5e-324 + * + * _.toNumber(Infinity); + * // => Infinity + * + * _.toNumber('3.2'); + * // => 3.2 + */ + function toNumber(value) { + if (typeof value == 'number') { + return value; + } + if (isSymbol(value)) { + return NAN; + } + if (isObject(value)) { + var other = typeof value.valueOf == 'function' ? value.valueOf() : value; + value = isObject(other) ? (other + '') : other; + } + if (typeof value != 'string') { + return value === 0 ? value : +value; + } + value = value.replace(reTrim, ''); + var isBinary = reIsBinary.test(value); + return (isBinary || reIsOctal.test(value)) + ? freeParseInt(value.slice(2), isBinary ? 2 : 8) + : (reIsBadHex.test(value) ? NAN : +value); + } + + /** + * Converts `value` to a plain object flattening inherited enumerable string + * keyed properties of `value` to own properties of the plain object. + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category Lang + * @param {*} value The value to convert. + * @returns {Object} Returns the converted plain object. + * @example + * + * function Foo() { + * this.b = 2; + * } + * + * Foo.prototype.c = 3; + * + * _.assign({ 'a': 1 }, new Foo); + * // => { 'a': 1, 'b': 2 } + * + * _.assign({ 'a': 1 }, _.toPlainObject(new Foo)); + * // => { 'a': 1, 'b': 2, 'c': 3 } + */ + function toPlainObject(value) { + return copyObject(value, keysIn(value)); + } + + /** + * Converts `value` to a safe integer. A safe integer can be compared and + * represented correctly. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to convert. + * @returns {number} Returns the converted integer. + * @example + * + * _.toSafeInteger(3.2); + * // => 3 + * + * _.toSafeInteger(Number.MIN_VALUE); + * // => 0 + * + * _.toSafeInteger(Infinity); + * // => 9007199254740991 + * + * _.toSafeInteger('3.2'); + * // => 3 + */ + function toSafeInteger(value) { + return value + ? baseClamp(toInteger(value), -MAX_SAFE_INTEGER, MAX_SAFE_INTEGER) + : (value === 0 ? value : 0); + } + + /** + * Converts `value` to a string. An empty string is returned for `null` + * and `undefined` values. The sign of `-0` is preserved. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to convert. + * @returns {string} Returns the converted string. + * @example + * + * _.toString(null); + * // => '' + * + * _.toString(-0); + * // => '-0' + * + * _.toString([1, 2, 3]); + * // => '1,2,3' + */ + function toString(value) { + return value == null ? '' : baseToString(value); + } + + /*------------------------------------------------------------------------*/ + + /** + * Assigns own enumerable string keyed properties of source objects to the + * destination object. Source objects are applied from left to right. + * Subsequent sources overwrite property assignments of previous sources. + * + * **Note:** This method mutates `object` and is loosely based on + * [`Object.assign`](https://mdn.io/Object/assign). + * + * @static + * @memberOf _ + * @since 0.10.0 + * @category Object + * @param {Object} object The destination object. + * @param {...Object} [sources] The source objects. + * @returns {Object} Returns `object`. + * @see _.assignIn + * @example + * + * function Foo() { + * this.a = 1; + * } + * + * function Bar() { + * this.c = 3; + * } + * + * Foo.prototype.b = 2; + * Bar.prototype.d = 4; + * + * _.assign({ 'a': 0 }, new Foo, new Bar); + * // => { 'a': 1, 'c': 3 } + */ + var assign = createAssigner(function(object, source) { + if (isPrototype(source) || isArrayLike(source)) { + copyObject(source, keys(source), object); + return; + } + for (var key in source) { + if (hasOwnProperty.call(source, key)) { + assignValue(object, key, source[key]); + } + } + }); + + /** + * This method is like `_.assign` except that it iterates over own and + * inherited source properties. + * + * **Note:** This method mutates `object`. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @alias extend + * @category Object + * @param {Object} object The destination object. + * @param {...Object} [sources] The source objects. + * @returns {Object} Returns `object`. + * @see _.assign + * @example + * + * function Foo() { + * this.a = 1; + * } + * + * function Bar() { + * this.c = 3; + * } + * + * Foo.prototype.b = 2; + * Bar.prototype.d = 4; + * + * _.assignIn({ 'a': 0 }, new Foo, new Bar); + * // => { 'a': 1, 'b': 2, 'c': 3, 'd': 4 } + */ + var assignIn = createAssigner(function(object, source) { + copyObject(source, keysIn(source), object); + }); + + /** + * This method is like `_.assignIn` except that it accepts `customizer` + * which is invoked to produce the assigned values. If `customizer` returns + * `undefined`, assignment is handled by the method instead. The `customizer` + * is invoked with five arguments: (objValue, srcValue, key, object, source). + * + * **Note:** This method mutates `object`. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @alias extendWith + * @category Object + * @param {Object} object The destination object. + * @param {...Object} sources The source objects. + * @param {Function} [customizer] The function to customize assigned values. + * @returns {Object} Returns `object`. + * @see _.assignWith + * @example + * + * function customizer(objValue, srcValue) { + * return _.isUndefined(objValue) ? srcValue : objValue; + * } + * + * var defaults = _.partialRight(_.assignInWith, customizer); + * + * defaults({ 'a': 1 }, { 'b': 2 }, { 'a': 3 }); + * // => { 'a': 1, 'b': 2 } + */ + var assignInWith = createAssigner(function(object, source, srcIndex, customizer) { + copyObject(source, keysIn(source), object, customizer); + }); + + /** + * This method is like `_.assign` except that it accepts `customizer` + * which is invoked to produce the assigned values. If `customizer` returns + * `undefined`, assignment is handled by the method instead. The `customizer` + * is invoked with five arguments: (objValue, srcValue, key, object, source). + * + * **Note:** This method mutates `object`. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Object + * @param {Object} object The destination object. + * @param {...Object} sources The source objects. + * @param {Function} [customizer] The function to customize assigned values. + * @returns {Object} Returns `object`. + * @see _.assignInWith + * @example + * + * function customizer(objValue, srcValue) { + * return _.isUndefined(objValue) ? srcValue : objValue; + * } + * + * var defaults = _.partialRight(_.assignWith, customizer); + * + * defaults({ 'a': 1 }, { 'b': 2 }, { 'a': 3 }); + * // => { 'a': 1, 'b': 2 } + */ + var assignWith = createAssigner(function(object, source, srcIndex, customizer) { + copyObject(source, keys(source), object, customizer); + }); + + /** + * Creates an array of values corresponding to `paths` of `object`. + * + * @static + * @memberOf _ + * @since 1.0.0 + * @category Object + * @param {Object} object The object to iterate over. + * @param {...(string|string[])} [paths] The property paths to pick. + * @returns {Array} Returns the picked values. + * @example + * + * var object = { 'a': [{ 'b': { 'c': 3 } }, 4] }; + * + * _.at(object, ['a[0].b.c', 'a[1]']); + * // => [3, 4] + */ + var at = flatRest(baseAt); + + /** + * Creates an object that inherits from the `prototype` object. If a + * `properties` object is given, its own enumerable string keyed properties + * are assigned to the created object. + * + * @static + * @memberOf _ + * @since 2.3.0 + * @category Object + * @param {Object} prototype The object to inherit from. + * @param {Object} [properties] The properties to assign to the object. + * @returns {Object} Returns the new object. + * @example + * + * function Shape() { + * this.x = 0; + * this.y = 0; + * } + * + * function Circle() { + * Shape.call(this); + * } + * + * Circle.prototype = _.create(Shape.prototype, { + * 'constructor': Circle + * }); + * + * var circle = new Circle; + * circle instanceof Circle; + * // => true + * + * circle instanceof Shape; + * // => true + */ + function create(prototype, properties) { + var result = baseCreate(prototype); + return properties == null ? result : baseAssign(result, properties); + } + + /** + * Assigns own and inherited enumerable string keyed properties of source + * objects to the destination object for all destination properties that + * resolve to `undefined`. Source objects are applied from left to right. + * Once a property is set, additional values of the same property are ignored. + * + * **Note:** This method mutates `object`. + * + * @static + * @since 0.1.0 + * @memberOf _ + * @category Object + * @param {Object} object The destination object. + * @param {...Object} [sources] The source objects. + * @returns {Object} Returns `object`. + * @see _.defaultsDeep + * @example + * + * _.defaults({ 'a': 1 }, { 'b': 2 }, { 'a': 3 }); + * // => { 'a': 1, 'b': 2 } + */ + var defaults = baseRest(function(args) { + args.push(undefined, customDefaultsAssignIn); + return apply(assignInWith, undefined, args); + }); + + /** + * This method is like `_.defaults` except that it recursively assigns + * default properties. + * + * **Note:** This method mutates `object`. + * + * @static + * @memberOf _ + * @since 3.10.0 + * @category Object + * @param {Object} object The destination object. + * @param {...Object} [sources] The source objects. + * @returns {Object} Returns `object`. + * @see _.defaults + * @example + * + * _.defaultsDeep({ 'a': { 'b': 2 } }, { 'a': { 'b': 1, 'c': 3 } }); + * // => { 'a': { 'b': 2, 'c': 3 } } + */ + var defaultsDeep = baseRest(function(args) { + args.push(undefined, customDefaultsMerge); + return apply(mergeWith, undefined, args); + }); + + /** + * This method is like `_.find` except that it returns the key of the first + * element `predicate` returns truthy for instead of the element itself. + * + * @static + * @memberOf _ + * @since 1.1.0 + * @category Object + * @param {Object} object The object to inspect. + * @param {Function} [predicate=_.identity] The function invoked per iteration. + * @returns {string|undefined} Returns the key of the matched element, + * else `undefined`. + * @example + * + * var users = { + * 'barney': { 'age': 36, 'active': true }, + * 'fred': { 'age': 40, 'active': false }, + * 'pebbles': { 'age': 1, 'active': true } + * }; + * + * _.findKey(users, function(o) { return o.age < 40; }); + * // => 'barney' (iteration order is not guaranteed) + * + * // The `_.matches` iteratee shorthand. + * _.findKey(users, { 'age': 1, 'active': true }); + * // => 'pebbles' + * + * // The `_.matchesProperty` iteratee shorthand. + * _.findKey(users, ['active', false]); + * // => 'fred' + * + * // The `_.property` iteratee shorthand. + * _.findKey(users, 'active'); + * // => 'barney' + */ + function findKey(object, predicate) { + return baseFindKey(object, getIteratee(predicate, 3), baseForOwn); + } + + /** + * This method is like `_.findKey` except that it iterates over elements of + * a collection in the opposite order. + * + * @static + * @memberOf _ + * @since 2.0.0 + * @category Object + * @param {Object} object The object to inspect. + * @param {Function} [predicate=_.identity] The function invoked per iteration. + * @returns {string|undefined} Returns the key of the matched element, + * else `undefined`. + * @example + * + * var users = { + * 'barney': { 'age': 36, 'active': true }, + * 'fred': { 'age': 40, 'active': false }, + * 'pebbles': { 'age': 1, 'active': true } + * }; + * + * _.findLastKey(users, function(o) { return o.age < 40; }); + * // => returns 'pebbles' assuming `_.findKey` returns 'barney' + * + * // The `_.matches` iteratee shorthand. + * _.findLastKey(users, { 'age': 36, 'active': true }); + * // => 'barney' + * + * // The `_.matchesProperty` iteratee shorthand. + * _.findLastKey(users, ['active', false]); + * // => 'fred' + * + * // The `_.property` iteratee shorthand. + * _.findLastKey(users, 'active'); + * // => 'pebbles' + */ + function findLastKey(object, predicate) { + return baseFindKey(object, getIteratee(predicate, 3), baseForOwnRight); + } + + /** + * Iterates over own and inherited enumerable string keyed properties of an + * object and invokes `iteratee` for each property. The iteratee is invoked + * with three arguments: (value, key, object). Iteratee functions may exit + * iteration early by explicitly returning `false`. + * + * @static + * @memberOf _ + * @since 0.3.0 + * @category Object + * @param {Object} object The object to iterate over. + * @param {Function} [iteratee=_.identity] The function invoked per iteration. + * @returns {Object} Returns `object`. + * @see _.forInRight + * @example + * + * function Foo() { + * this.a = 1; + * this.b = 2; + * } + * + * Foo.prototype.c = 3; + * + * _.forIn(new Foo, function(value, key) { + * console.log(key); + * }); + * // => Logs 'a', 'b', then 'c' (iteration order is not guaranteed). + */ + function forIn(object, iteratee) { + return object == null + ? object + : baseFor(object, getIteratee(iteratee, 3), keysIn); + } + + /** + * This method is like `_.forIn` except that it iterates over properties of + * `object` in the opposite order. + * + * @static + * @memberOf _ + * @since 2.0.0 + * @category Object + * @param {Object} object The object to iterate over. + * @param {Function} [iteratee=_.identity] The function invoked per iteration. + * @returns {Object} Returns `object`. + * @see _.forIn + * @example + * + * function Foo() { + * this.a = 1; + * this.b = 2; + * } + * + * Foo.prototype.c = 3; + * + * _.forInRight(new Foo, function(value, key) { + * console.log(key); + * }); + * // => Logs 'c', 'b', then 'a' assuming `_.forIn` logs 'a', 'b', then 'c'. + */ + function forInRight(object, iteratee) { + return object == null + ? object + : baseForRight(object, getIteratee(iteratee, 3), keysIn); + } + + /** + * Iterates over own enumerable string keyed properties of an object and + * invokes `iteratee` for each property. The iteratee is invoked with three + * arguments: (value, key, object). Iteratee functions may exit iteration + * early by explicitly returning `false`. + * + * @static + * @memberOf _ + * @since 0.3.0 + * @category Object + * @param {Object} object The object to iterate over. + * @param {Function} [iteratee=_.identity] The function invoked per iteration. + * @returns {Object} Returns `object`. + * @see _.forOwnRight + * @example + * + * function Foo() { + * this.a = 1; + * this.b = 2; + * } + * + * Foo.prototype.c = 3; + * + * _.forOwn(new Foo, function(value, key) { + * console.log(key); + * }); + * // => Logs 'a' then 'b' (iteration order is not guaranteed). + */ + function forOwn(object, iteratee) { + return object && baseForOwn(object, getIteratee(iteratee, 3)); + } + + /** + * This method is like `_.forOwn` except that it iterates over properties of + * `object` in the opposite order. + * + * @static + * @memberOf _ + * @since 2.0.0 + * @category Object + * @param {Object} object The object to iterate over. + * @param {Function} [iteratee=_.identity] The function invoked per iteration. + * @returns {Object} Returns `object`. + * @see _.forOwn + * @example + * + * function Foo() { + * this.a = 1; + * this.b = 2; + * } + * + * Foo.prototype.c = 3; + * + * _.forOwnRight(new Foo, function(value, key) { + * console.log(key); + * }); + * // => Logs 'b' then 'a' assuming `_.forOwn` logs 'a' then 'b'. + */ + function forOwnRight(object, iteratee) { + return object && baseForOwnRight(object, getIteratee(iteratee, 3)); + } + + /** + * Creates an array of function property names from own enumerable properties + * of `object`. + * + * @static + * @since 0.1.0 + * @memberOf _ + * @category Object + * @param {Object} object The object to inspect. + * @returns {Array} Returns the function names. + * @see _.functionsIn + * @example + * + * function Foo() { + * this.a = _.constant('a'); + * this.b = _.constant('b'); + * } + * + * Foo.prototype.c = _.constant('c'); + * + * _.functions(new Foo); + * // => ['a', 'b'] + */ + function functions(object) { + return object == null ? [] : baseFunctions(object, keys(object)); + } + + /** + * Creates an array of function property names from own and inherited + * enumerable properties of `object`. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Object + * @param {Object} object The object to inspect. + * @returns {Array} Returns the function names. + * @see _.functions + * @example + * + * function Foo() { + * this.a = _.constant('a'); + * this.b = _.constant('b'); + * } + * + * Foo.prototype.c = _.constant('c'); + * + * _.functionsIn(new Foo); + * // => ['a', 'b', 'c'] + */ + function functionsIn(object) { + return object == null ? [] : baseFunctions(object, keysIn(object)); + } + + /** + * Gets the value at `path` of `object`. If the resolved value is + * `undefined`, the `defaultValue` is returned in its place. + * + * @static + * @memberOf _ + * @since 3.7.0 + * @category Object + * @param {Object} object The object to query. + * @param {Array|string} path The path of the property to get. + * @param {*} [defaultValue] The value returned for `undefined` resolved values. + * @returns {*} Returns the resolved value. + * @example + * + * var object = { 'a': [{ 'b': { 'c': 3 } }] }; + * + * _.get(object, 'a[0].b.c'); + * // => 3 + * + * _.get(object, ['a', '0', 'b', 'c']); + * // => 3 + * + * _.get(object, 'a.b.c', 'default'); + * // => 'default' + */ + function get(object, path, defaultValue) { + var result = object == null ? undefined : baseGet(object, path); + return result === undefined ? defaultValue : result; + } + + /** + * Checks if `path` is a direct property of `object`. + * + * @static + * @since 0.1.0 + * @memberOf _ + * @category Object + * @param {Object} object The object to query. + * @param {Array|string} path The path to check. + * @returns {boolean} Returns `true` if `path` exists, else `false`. + * @example + * + * var object = { 'a': { 'b': 2 } }; + * var other = _.create({ 'a': _.create({ 'b': 2 }) }); + * + * _.has(object, 'a'); + * // => true + * + * _.has(object, 'a.b'); + * // => true + * + * _.has(object, ['a', 'b']); + * // => true + * + * _.has(other, 'a'); + * // => false + */ + function has(object, path) { + return object != null && hasPath(object, path, baseHas); + } + + /** + * Checks if `path` is a direct or inherited property of `object`. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Object + * @param {Object} object The object to query. + * @param {Array|string} path The path to check. + * @returns {boolean} Returns `true` if `path` exists, else `false`. + * @example + * + * var object = _.create({ 'a': _.create({ 'b': 2 }) }); + * + * _.hasIn(object, 'a'); + * // => true + * + * _.hasIn(object, 'a.b'); + * // => true + * + * _.hasIn(object, ['a', 'b']); + * // => true + * + * _.hasIn(object, 'b'); + * // => false + */ + function hasIn(object, path) { + return object != null && hasPath(object, path, baseHasIn); + } + + /** + * Creates an object composed of the inverted keys and values of `object`. + * If `object` contains duplicate values, subsequent values overwrite + * property assignments of previous values. + * + * @static + * @memberOf _ + * @since 0.7.0 + * @category Object + * @param {Object} object The object to invert. + * @returns {Object} Returns the new inverted object. + * @example + * + * var object = { 'a': 1, 'b': 2, 'c': 1 }; + * + * _.invert(object); + * // => { '1': 'c', '2': 'b' } + */ + var invert = createInverter(function(result, value, key) { + result[value] = key; + }, constant(identity)); + + /** + * This method is like `_.invert` except that the inverted object is generated + * from the results of running each element of `object` thru `iteratee`. The + * corresponding inverted value of each inverted key is an array of keys + * responsible for generating the inverted value. The iteratee is invoked + * with one argument: (value). + * + * @static + * @memberOf _ + * @since 4.1.0 + * @category Object + * @param {Object} object The object to invert. + * @param {Function} [iteratee=_.identity] The iteratee invoked per element. + * @returns {Object} Returns the new inverted object. + * @example + * + * var object = { 'a': 1, 'b': 2, 'c': 1 }; + * + * _.invertBy(object); + * // => { '1': ['a', 'c'], '2': ['b'] } + * + * _.invertBy(object, function(value) { + * return 'group' + value; + * }); + * // => { 'group1': ['a', 'c'], 'group2': ['b'] } + */ + var invertBy = createInverter(function(result, value, key) { + if (hasOwnProperty.call(result, value)) { + result[value].push(key); + } else { + result[value] = [key]; + } + }, getIteratee); + + /** + * Invokes the method at `path` of `object`. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Object + * @param {Object} object The object to query. + * @param {Array|string} path The path of the method to invoke. + * @param {...*} [args] The arguments to invoke the method with. + * @returns {*} Returns the result of the invoked method. + * @example + * + * var object = { 'a': [{ 'b': { 'c': [1, 2, 3, 4] } }] }; + * + * _.invoke(object, 'a[0].b.c.slice', 1, 3); + * // => [2, 3] + */ + var invoke = baseRest(baseInvoke); + + /** + * Creates an array of the own enumerable property names of `object`. + * + * **Note:** Non-object values are coerced to objects. See the + * [ES spec](http://ecma-international.org/ecma-262/7.0/#sec-object.keys) + * for more details. + * + * @static + * @since 0.1.0 + * @memberOf _ + * @category Object + * @param {Object} object The object to query. + * @returns {Array} Returns the array of property names. + * @example + * + * function Foo() { + * this.a = 1; + * this.b = 2; + * } + * + * Foo.prototype.c = 3; + * + * _.keys(new Foo); + * // => ['a', 'b'] (iteration order is not guaranteed) + * + * _.keys('hi'); + * // => ['0', '1'] + */ + function keys(object) { + return isArrayLike(object) ? arrayLikeKeys(object) : baseKeys(object); + } + + /** + * Creates an array of the own and inherited enumerable property names of `object`. + * + * **Note:** Non-object values are coerced to objects. + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category Object + * @param {Object} object The object to query. + * @returns {Array} Returns the array of property names. + * @example + * + * function Foo() { + * this.a = 1; + * this.b = 2; + * } + * + * Foo.prototype.c = 3; + * + * _.keysIn(new Foo); + * // => ['a', 'b', 'c'] (iteration order is not guaranteed) + */ + function keysIn(object) { + return isArrayLike(object) ? arrayLikeKeys(object, true) : baseKeysIn(object); + } + + /** + * The opposite of `_.mapValues`; this method creates an object with the + * same values as `object` and keys generated by running each own enumerable + * string keyed property of `object` thru `iteratee`. The iteratee is invoked + * with three arguments: (value, key, object). + * + * @static + * @memberOf _ + * @since 3.8.0 + * @category Object + * @param {Object} object The object to iterate over. + * @param {Function} [iteratee=_.identity] The function invoked per iteration. + * @returns {Object} Returns the new mapped object. + * @see _.mapValues + * @example + * + * _.mapKeys({ 'a': 1, 'b': 2 }, function(value, key) { + * return key + value; + * }); + * // => { 'a1': 1, 'b2': 2 } + */ + function mapKeys(object, iteratee) { + var result = {}; + iteratee = getIteratee(iteratee, 3); + + baseForOwn(object, function(value, key, object) { + baseAssignValue(result, iteratee(value, key, object), value); + }); + return result; + } + + /** + * Creates an object with the same keys as `object` and values generated + * by running each own enumerable string keyed property of `object` thru + * `iteratee`. The iteratee is invoked with three arguments: + * (value, key, object). + * + * @static + * @memberOf _ + * @since 2.4.0 + * @category Object + * @param {Object} object The object to iterate over. + * @param {Function} [iteratee=_.identity] The function invoked per iteration. + * @returns {Object} Returns the new mapped object. + * @see _.mapKeys + * @example + * + * var users = { + * 'fred': { 'user': 'fred', 'age': 40 }, + * 'pebbles': { 'user': 'pebbles', 'age': 1 } + * }; + * + * _.mapValues(users, function(o) { return o.age; }); + * // => { 'fred': 40, 'pebbles': 1 } (iteration order is not guaranteed) + * + * // The `_.property` iteratee shorthand. + * _.mapValues(users, 'age'); + * // => { 'fred': 40, 'pebbles': 1 } (iteration order is not guaranteed) + */ + function mapValues(object, iteratee) { + var result = {}; + iteratee = getIteratee(iteratee, 3); + + baseForOwn(object, function(value, key, object) { + baseAssignValue(result, key, iteratee(value, key, object)); + }); + return result; + } + + /** + * This method is like `_.assign` except that it recursively merges own and + * inherited enumerable string keyed properties of source objects into the + * destination object. Source properties that resolve to `undefined` are + * skipped if a destination value exists. Array and plain object properties + * are merged recursively. Other objects and value types are overridden by + * assignment. Source objects are applied from left to right. Subsequent + * sources overwrite property assignments of previous sources. + * + * **Note:** This method mutates `object`. + * + * @static + * @memberOf _ + * @since 0.5.0 + * @category Object + * @param {Object} object The destination object. + * @param {...Object} [sources] The source objects. + * @returns {Object} Returns `object`. + * @example + * + * var object = { + * 'a': [{ 'b': 2 }, { 'd': 4 }] + * }; + * + * var other = { + * 'a': [{ 'c': 3 }, { 'e': 5 }] + * }; + * + * _.merge(object, other); + * // => { 'a': [{ 'b': 2, 'c': 3 }, { 'd': 4, 'e': 5 }] } + */ + var merge = createAssigner(function(object, source, srcIndex) { + baseMerge(object, source, srcIndex); + }); + + /** + * This method is like `_.merge` except that it accepts `customizer` which + * is invoked to produce the merged values of the destination and source + * properties. If `customizer` returns `undefined`, merging is handled by the + * method instead. The `customizer` is invoked with six arguments: + * (objValue, srcValue, key, object, source, stack). + * + * **Note:** This method mutates `object`. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Object + * @param {Object} object The destination object. + * @param {...Object} sources The source objects. + * @param {Function} customizer The function to customize assigned values. + * @returns {Object} Returns `object`. + * @example + * + * function customizer(objValue, srcValue) { + * if (_.isArray(objValue)) { + * return objValue.concat(srcValue); + * } + * } + * + * var object = { 'a': [1], 'b': [2] }; + * var other = { 'a': [3], 'b': [4] }; + * + * _.mergeWith(object, other, customizer); + * // => { 'a': [1, 3], 'b': [2, 4] } + */ + var mergeWith = createAssigner(function(object, source, srcIndex, customizer) { + baseMerge(object, source, srcIndex, customizer); + }); + + /** + * The opposite of `_.pick`; this method creates an object composed of the + * own and inherited enumerable property paths of `object` that are not omitted. + * + * **Note:** This method is considerably slower than `_.pick`. + * + * @static + * @since 0.1.0 + * @memberOf _ + * @category Object + * @param {Object} object The source object. + * @param {...(string|string[])} [paths] The property paths to omit. + * @returns {Object} Returns the new object. + * @example + * + * var object = { 'a': 1, 'b': '2', 'c': 3 }; + * + * _.omit(object, ['a', 'c']); + * // => { 'b': '2' } + */ + var omit = flatRest(function(object, paths) { + var result = {}; + if (object == null) { + return result; + } + var isDeep = false; + paths = arrayMap(paths, function(path) { + path = castPath(path, object); + isDeep || (isDeep = path.length > 1); + return path; + }); + copyObject(object, getAllKeysIn(object), result); + if (isDeep) { + result = baseClone(result, CLONE_DEEP_FLAG | CLONE_FLAT_FLAG | CLONE_SYMBOLS_FLAG, customOmitClone); + } + var length = paths.length; + while (length--) { + baseUnset(result, paths[length]); + } + return result; + }); + + /** + * The opposite of `_.pickBy`; this method creates an object composed of + * the own and inherited enumerable string keyed properties of `object` that + * `predicate` doesn't return truthy for. The predicate is invoked with two + * arguments: (value, key). + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Object + * @param {Object} object The source object. + * @param {Function} [predicate=_.identity] The function invoked per property. + * @returns {Object} Returns the new object. + * @example + * + * var object = { 'a': 1, 'b': '2', 'c': 3 }; + * + * _.omitBy(object, _.isNumber); + * // => { 'b': '2' } + */ + function omitBy(object, predicate) { + return pickBy(object, negate(getIteratee(predicate))); + } + + /** + * Creates an object composed of the picked `object` properties. + * + * @static + * @since 0.1.0 + * @memberOf _ + * @category Object + * @param {Object} object The source object. + * @param {...(string|string[])} [paths] The property paths to pick. + * @returns {Object} Returns the new object. + * @example + * + * var object = { 'a': 1, 'b': '2', 'c': 3 }; + * + * _.pick(object, ['a', 'c']); + * // => { 'a': 1, 'c': 3 } + */ + var pick = flatRest(function(object, paths) { + return object == null ? {} : basePick(object, paths); + }); + + /** + * Creates an object composed of the `object` properties `predicate` returns + * truthy for. The predicate is invoked with two arguments: (value, key). + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Object + * @param {Object} object The source object. + * @param {Function} [predicate=_.identity] The function invoked per property. + * @returns {Object} Returns the new object. + * @example + * + * var object = { 'a': 1, 'b': '2', 'c': 3 }; + * + * _.pickBy(object, _.isNumber); + * // => { 'a': 1, 'c': 3 } + */ + function pickBy(object, predicate) { + if (object == null) { + return {}; + } + var props = arrayMap(getAllKeysIn(object), function(prop) { + return [prop]; + }); + predicate = getIteratee(predicate); + return basePickBy(object, props, function(value, path) { + return predicate(value, path[0]); + }); + } + + /** + * This method is like `_.get` except that if the resolved value is a + * function it's invoked with the `this` binding of its parent object and + * its result is returned. + * + * @static + * @since 0.1.0 + * @memberOf _ + * @category Object + * @param {Object} object The object to query. + * @param {Array|string} path The path of the property to resolve. + * @param {*} [defaultValue] The value returned for `undefined` resolved values. + * @returns {*} Returns the resolved value. + * @example + * + * var object = { 'a': [{ 'b': { 'c1': 3, 'c2': _.constant(4) } }] }; + * + * _.result(object, 'a[0].b.c1'); + * // => 3 + * + * _.result(object, 'a[0].b.c2'); + * // => 4 + * + * _.result(object, 'a[0].b.c3', 'default'); + * // => 'default' + * + * _.result(object, 'a[0].b.c3', _.constant('default')); + * // => 'default' + */ + function result(object, path, defaultValue) { + path = castPath(path, object); + + var index = -1, + length = path.length; + + // Ensure the loop is entered when path is empty. + if (!length) { + length = 1; + object = undefined; + } + while (++index < length) { + var value = object == null ? undefined : object[toKey(path[index])]; + if (value === undefined) { + index = length; + value = defaultValue; + } + object = isFunction(value) ? value.call(object) : value; + } + return object; + } + + /** + * Sets the value at `path` of `object`. If a portion of `path` doesn't exist, + * it's created. Arrays are created for missing index properties while objects + * are created for all other missing properties. Use `_.setWith` to customize + * `path` creation. + * + * **Note:** This method mutates `object`. + * + * @static + * @memberOf _ + * @since 3.7.0 + * @category Object + * @param {Object} object The object to modify. + * @param {Array|string} path The path of the property to set. + * @param {*} value The value to set. + * @returns {Object} Returns `object`. + * @example + * + * var object = { 'a': [{ 'b': { 'c': 3 } }] }; + * + * _.set(object, 'a[0].b.c', 4); + * console.log(object.a[0].b.c); + * // => 4 + * + * _.set(object, ['x', '0', 'y', 'z'], 5); + * console.log(object.x[0].y.z); + * // => 5 + */ + function set(object, path, value) { + return object == null ? object : baseSet(object, path, value); + } + + /** + * This method is like `_.set` except that it accepts `customizer` which is + * invoked to produce the objects of `path`. If `customizer` returns `undefined` + * path creation is handled by the method instead. The `customizer` is invoked + * with three arguments: (nsValue, key, nsObject). + * + * **Note:** This method mutates `object`. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Object + * @param {Object} object The object to modify. + * @param {Array|string} path The path of the property to set. + * @param {*} value The value to set. + * @param {Function} [customizer] The function to customize assigned values. + * @returns {Object} Returns `object`. + * @example + * + * var object = {}; + * + * _.setWith(object, '[0][1]', 'a', Object); + * // => { '0': { '1': 'a' } } + */ + function setWith(object, path, value, customizer) { + customizer = typeof customizer == 'function' ? customizer : undefined; + return object == null ? object : baseSet(object, path, value, customizer); + } + + /** + * Creates an array of own enumerable string keyed-value pairs for `object` + * which can be consumed by `_.fromPairs`. If `object` is a map or set, its + * entries are returned. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @alias entries + * @category Object + * @param {Object} object The object to query. + * @returns {Array} Returns the key-value pairs. + * @example + * + * function Foo() { + * this.a = 1; + * this.b = 2; + * } + * + * Foo.prototype.c = 3; + * + * _.toPairs(new Foo); + * // => [['a', 1], ['b', 2]] (iteration order is not guaranteed) + */ + var toPairs = createToPairs(keys); + + /** + * Creates an array of own and inherited enumerable string keyed-value pairs + * for `object` which can be consumed by `_.fromPairs`. If `object` is a map + * or set, its entries are returned. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @alias entriesIn + * @category Object + * @param {Object} object The object to query. + * @returns {Array} Returns the key-value pairs. + * @example + * + * function Foo() { + * this.a = 1; + * this.b = 2; + * } + * + * Foo.prototype.c = 3; + * + * _.toPairsIn(new Foo); + * // => [['a', 1], ['b', 2], ['c', 3]] (iteration order is not guaranteed) + */ + var toPairsIn = createToPairs(keysIn); + + /** + * An alternative to `_.reduce`; this method transforms `object` to a new + * `accumulator` object which is the result of running each of its own + * enumerable string keyed properties thru `iteratee`, with each invocation + * potentially mutating the `accumulator` object. If `accumulator` is not + * provided, a new object with the same `[[Prototype]]` will be used. The + * iteratee is invoked with four arguments: (accumulator, value, key, object). + * Iteratee functions may exit iteration early by explicitly returning `false`. + * + * @static + * @memberOf _ + * @since 1.3.0 + * @category Object + * @param {Object} object The object to iterate over. + * @param {Function} [iteratee=_.identity] The function invoked per iteration. + * @param {*} [accumulator] The custom accumulator value. + * @returns {*} Returns the accumulated value. + * @example + * + * _.transform([2, 3, 4], function(result, n) { + * result.push(n *= n); + * return n % 2 == 0; + * }, []); + * // => [4, 9] + * + * _.transform({ 'a': 1, 'b': 2, 'c': 1 }, function(result, value, key) { + * (result[value] || (result[value] = [])).push(key); + * }, {}); + * // => { '1': ['a', 'c'], '2': ['b'] } + */ + function transform(object, iteratee, accumulator) { + var isArr = isArray(object), + isArrLike = isArr || isBuffer(object) || isTypedArray(object); + + iteratee = getIteratee(iteratee, 4); + if (accumulator == null) { + var Ctor = object && object.constructor; + if (isArrLike) { + accumulator = isArr ? new Ctor : []; + } + else if (isObject(object)) { + accumulator = isFunction(Ctor) ? baseCreate(getPrototype(object)) : {}; + } + else { + accumulator = {}; + } + } + (isArrLike ? arrayEach : baseForOwn)(object, function(value, index, object) { + return iteratee(accumulator, value, index, object); + }); + return accumulator; + } + + /** + * Removes the property at `path` of `object`. + * + * **Note:** This method mutates `object`. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Object + * @param {Object} object The object to modify. + * @param {Array|string} path The path of the property to unset. + * @returns {boolean} Returns `true` if the property is deleted, else `false`. + * @example + * + * var object = { 'a': [{ 'b': { 'c': 7 } }] }; + * _.unset(object, 'a[0].b.c'); + * // => true + * + * console.log(object); + * // => { 'a': [{ 'b': {} }] }; + * + * _.unset(object, ['a', '0', 'b', 'c']); + * // => true + * + * console.log(object); + * // => { 'a': [{ 'b': {} }] }; + */ + function unset(object, path) { + return object == null ? true : baseUnset(object, path); + } + + /** + * This method is like `_.set` except that accepts `updater` to produce the + * value to set. Use `_.updateWith` to customize `path` creation. The `updater` + * is invoked with one argument: (value). + * + * **Note:** This method mutates `object`. + * + * @static + * @memberOf _ + * @since 4.6.0 + * @category Object + * @param {Object} object The object to modify. + * @param {Array|string} path The path of the property to set. + * @param {Function} updater The function to produce the updated value. + * @returns {Object} Returns `object`. + * @example + * + * var object = { 'a': [{ 'b': { 'c': 3 } }] }; + * + * _.update(object, 'a[0].b.c', function(n) { return n * n; }); + * console.log(object.a[0].b.c); + * // => 9 + * + * _.update(object, 'x[0].y.z', function(n) { return n ? n + 1 : 0; }); + * console.log(object.x[0].y.z); + * // => 0 + */ + function update(object, path, updater) { + return object == null ? object : baseUpdate(object, path, castFunction(updater)); + } + + /** + * This method is like `_.update` except that it accepts `customizer` which is + * invoked to produce the objects of `path`. If `customizer` returns `undefined` + * path creation is handled by the method instead. The `customizer` is invoked + * with three arguments: (nsValue, key, nsObject). + * + * **Note:** This method mutates `object`. + * + * @static + * @memberOf _ + * @since 4.6.0 + * @category Object + * @param {Object} object The object to modify. + * @param {Array|string} path The path of the property to set. + * @param {Function} updater The function to produce the updated value. + * @param {Function} [customizer] The function to customize assigned values. + * @returns {Object} Returns `object`. + * @example + * + * var object = {}; + * + * _.updateWith(object, '[0][1]', _.constant('a'), Object); + * // => { '0': { '1': 'a' } } + */ + function updateWith(object, path, updater, customizer) { + customizer = typeof customizer == 'function' ? customizer : undefined; + return object == null ? object : baseUpdate(object, path, castFunction(updater), customizer); + } + + /** + * Creates an array of the own enumerable string keyed property values of `object`. + * + * **Note:** Non-object values are coerced to objects. + * + * @static + * @since 0.1.0 + * @memberOf _ + * @category Object + * @param {Object} object The object to query. + * @returns {Array} Returns the array of property values. + * @example + * + * function Foo() { + * this.a = 1; + * this.b = 2; + * } + * + * Foo.prototype.c = 3; + * + * _.values(new Foo); + * // => [1, 2] (iteration order is not guaranteed) + * + * _.values('hi'); + * // => ['h', 'i'] + */ + function values(object) { + return object == null ? [] : baseValues(object, keys(object)); + } + + /** + * Creates an array of the own and inherited enumerable string keyed property + * values of `object`. + * + * **Note:** Non-object values are coerced to objects. + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category Object + * @param {Object} object The object to query. + * @returns {Array} Returns the array of property values. + * @example + * + * function Foo() { + * this.a = 1; + * this.b = 2; + * } + * + * Foo.prototype.c = 3; + * + * _.valuesIn(new Foo); + * // => [1, 2, 3] (iteration order is not guaranteed) + */ + function valuesIn(object) { + return object == null ? [] : baseValues(object, keysIn(object)); + } + + /*------------------------------------------------------------------------*/ + + /** + * Clamps `number` within the inclusive `lower` and `upper` bounds. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Number + * @param {number} number The number to clamp. + * @param {number} [lower] The lower bound. + * @param {number} upper The upper bound. + * @returns {number} Returns the clamped number. + * @example + * + * _.clamp(-10, -5, 5); + * // => -5 + * + * _.clamp(10, -5, 5); + * // => 5 + */ + function clamp(number, lower, upper) { + if (upper === undefined) { + upper = lower; + lower = undefined; + } + if (upper !== undefined) { + upper = toNumber(upper); + upper = upper === upper ? upper : 0; + } + if (lower !== undefined) { + lower = toNumber(lower); + lower = lower === lower ? lower : 0; + } + return baseClamp(toNumber(number), lower, upper); + } + + /** + * Checks if `n` is between `start` and up to, but not including, `end`. If + * `end` is not specified, it's set to `start` with `start` then set to `0`. + * If `start` is greater than `end` the params are swapped to support + * negative ranges. + * + * @static + * @memberOf _ + * @since 3.3.0 + * @category Number + * @param {number} number The number to check. + * @param {number} [start=0] The start of the range. + * @param {number} end The end of the range. + * @returns {boolean} Returns `true` if `number` is in the range, else `false`. + * @see _.range, _.rangeRight + * @example + * + * _.inRange(3, 2, 4); + * // => true + * + * _.inRange(4, 8); + * // => true + * + * _.inRange(4, 2); + * // => false + * + * _.inRange(2, 2); + * // => false + * + * _.inRange(1.2, 2); + * // => true + * + * _.inRange(5.2, 4); + * // => false + * + * _.inRange(-3, -2, -6); + * // => true + */ + function inRange(number, start, end) { + start = toFinite(start); + if (end === undefined) { + end = start; + start = 0; + } else { + end = toFinite(end); + } + number = toNumber(number); + return baseInRange(number, start, end); + } + + /** + * Produces a random number between the inclusive `lower` and `upper` bounds. + * If only one argument is provided a number between `0` and the given number + * is returned. If `floating` is `true`, or either `lower` or `upper` are + * floats, a floating-point number is returned instead of an integer. + * + * **Note:** JavaScript follows the IEEE-754 standard for resolving + * floating-point values which can produce unexpected results. + * + * @static + * @memberOf _ + * @since 0.7.0 + * @category Number + * @param {number} [lower=0] The lower bound. + * @param {number} [upper=1] The upper bound. + * @param {boolean} [floating] Specify returning a floating-point number. + * @returns {number} Returns the random number. + * @example + * + * _.random(0, 5); + * // => an integer between 0 and 5 + * + * _.random(5); + * // => also an integer between 0 and 5 + * + * _.random(5, true); + * // => a floating-point number between 0 and 5 + * + * _.random(1.2, 5.2); + * // => a floating-point number between 1.2 and 5.2 + */ + function random(lower, upper, floating) { + if (floating && typeof floating != 'boolean' && isIterateeCall(lower, upper, floating)) { + upper = floating = undefined; + } + if (floating === undefined) { + if (typeof upper == 'boolean') { + floating = upper; + upper = undefined; + } + else if (typeof lower == 'boolean') { + floating = lower; + lower = undefined; + } + } + if (lower === undefined && upper === undefined) { + lower = 0; + upper = 1; + } + else { + lower = toFinite(lower); + if (upper === undefined) { + upper = lower; + lower = 0; + } else { + upper = toFinite(upper); + } + } + if (lower > upper) { + var temp = lower; + lower = upper; + upper = temp; + } + if (floating || lower % 1 || upper % 1) { + var rand = nativeRandom(); + return nativeMin(lower + (rand * (upper - lower + freeParseFloat('1e-' + ((rand + '').length - 1)))), upper); + } + return baseRandom(lower, upper); + } + + /*------------------------------------------------------------------------*/ + + /** + * Converts `string` to [camel case](https://en.wikipedia.org/wiki/CamelCase). + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category String + * @param {string} [string=''] The string to convert. + * @returns {string} Returns the camel cased string. + * @example + * + * _.camelCase('Foo Bar'); + * // => 'fooBar' + * + * _.camelCase('--foo-bar--'); + * // => 'fooBar' + * + * _.camelCase('__FOO_BAR__'); + * // => 'fooBar' + */ + var camelCase = createCompounder(function(result, word, index) { + word = word.toLowerCase(); + return result + (index ? capitalize(word) : word); + }); + + /** + * Converts the first character of `string` to upper case and the remaining + * to lower case. + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category String + * @param {string} [string=''] The string to capitalize. + * @returns {string} Returns the capitalized string. + * @example + * + * _.capitalize('FRED'); + * // => 'Fred' + */ + function capitalize(string) { + return upperFirst(toString(string).toLowerCase()); + } + + /** + * Deburrs `string` by converting + * [Latin-1 Supplement](https://en.wikipedia.org/wiki/Latin-1_Supplement_(Unicode_block)#Character_table) + * and [Latin Extended-A](https://en.wikipedia.org/wiki/Latin_Extended-A) + * letters to basic Latin letters and removing + * [combining diacritical marks](https://en.wikipedia.org/wiki/Combining_Diacritical_Marks). + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category String + * @param {string} [string=''] The string to deburr. + * @returns {string} Returns the deburred string. + * @example + * + * _.deburr('déjà vu'); + * // => 'deja vu' + */ + function deburr(string) { + string = toString(string); + return string && string.replace(reLatin, deburrLetter).replace(reComboMark, ''); + } + + /** + * Checks if `string` ends with the given target string. + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category String + * @param {string} [string=''] The string to inspect. + * @param {string} [target] The string to search for. + * @param {number} [position=string.length] The position to search up to. + * @returns {boolean} Returns `true` if `string` ends with `target`, + * else `false`. + * @example + * + * _.endsWith('abc', 'c'); + * // => true + * + * _.endsWith('abc', 'b'); + * // => false + * + * _.endsWith('abc', 'b', 2); + * // => true + */ + function endsWith(string, target, position) { + string = toString(string); + target = baseToString(target); + + var length = string.length; + position = position === undefined + ? length + : baseClamp(toInteger(position), 0, length); + + var end = position; + position -= target.length; + return position >= 0 && string.slice(position, end) == target; + } + + /** + * Converts the characters "&", "<", ">", '"', and "'" in `string` to their + * corresponding HTML entities. + * + * **Note:** No other characters are escaped. To escape additional + * characters use a third-party library like [_he_](https://mths.be/he). + * + * Though the ">" character is escaped for symmetry, characters like + * ">" and "/" don't need escaping in HTML and have no special meaning + * unless they're part of a tag or unquoted attribute value. See + * [Mathias Bynens's article](https://mathiasbynens.be/notes/ambiguous-ampersands) + * (under "semi-related fun fact") for more details. + * + * When working with HTML you should always + * [quote attribute values](http://wonko.com/post/html-escaping) to reduce + * XSS vectors. + * + * @static + * @since 0.1.0 + * @memberOf _ + * @category String + * @param {string} [string=''] The string to escape. + * @returns {string} Returns the escaped string. + * @example + * + * _.escape('fred, barney, & pebbles'); + * // => 'fred, barney, & pebbles' + */ + function escape(string) { + string = toString(string); + return (string && reHasUnescapedHtml.test(string)) + ? string.replace(reUnescapedHtml, escapeHtmlChar) + : string; + } + + /** + * Escapes the `RegExp` special characters "^", "$", "\", ".", "*", "+", + * "?", "(", ")", "[", "]", "{", "}", and "|" in `string`. + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category String + * @param {string} [string=''] The string to escape. + * @returns {string} Returns the escaped string. + * @example + * + * _.escapeRegExp('[lodash](https://lodash.com/)'); + * // => '\[lodash\]\(https://lodash\.com/\)' + */ + function escapeRegExp(string) { + string = toString(string); + return (string && reHasRegExpChar.test(string)) + ? string.replace(reRegExpChar, '\\$&') + : string; + } + + /** + * Converts `string` to + * [kebab case](https://en.wikipedia.org/wiki/Letter_case#Special_case_styles). + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category String + * @param {string} [string=''] The string to convert. + * @returns {string} Returns the kebab cased string. + * @example + * + * _.kebabCase('Foo Bar'); + * // => 'foo-bar' + * + * _.kebabCase('fooBar'); + * // => 'foo-bar' + * + * _.kebabCase('__FOO_BAR__'); + * // => 'foo-bar' + */ + var kebabCase = createCompounder(function(result, word, index) { + return result + (index ? '-' : '') + word.toLowerCase(); + }); + + /** + * Converts `string`, as space separated words, to lower case. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category String + * @param {string} [string=''] The string to convert. + * @returns {string} Returns the lower cased string. + * @example + * + * _.lowerCase('--Foo-Bar--'); + * // => 'foo bar' + * + * _.lowerCase('fooBar'); + * // => 'foo bar' + * + * _.lowerCase('__FOO_BAR__'); + * // => 'foo bar' + */ + var lowerCase = createCompounder(function(result, word, index) { + return result + (index ? ' ' : '') + word.toLowerCase(); + }); + + /** + * Converts the first character of `string` to lower case. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category String + * @param {string} [string=''] The string to convert. + * @returns {string} Returns the converted string. + * @example + * + * _.lowerFirst('Fred'); + * // => 'fred' + * + * _.lowerFirst('FRED'); + * // => 'fRED' + */ + var lowerFirst = createCaseFirst('toLowerCase'); + + /** + * Pads `string` on the left and right sides if it's shorter than `length`. + * Padding characters are truncated if they can't be evenly divided by `length`. + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category String + * @param {string} [string=''] The string to pad. + * @param {number} [length=0] The padding length. + * @param {string} [chars=' '] The string used as padding. + * @returns {string} Returns the padded string. + * @example + * + * _.pad('abc', 8); + * // => ' abc ' + * + * _.pad('abc', 8, '_-'); + * // => '_-abc_-_' + * + * _.pad('abc', 3); + * // => 'abc' + */ + function pad(string, length, chars) { + string = toString(string); + length = toInteger(length); + + var strLength = length ? stringSize(string) : 0; + if (!length || strLength >= length) { + return string; + } + var mid = (length - strLength) / 2; + return ( + createPadding(nativeFloor(mid), chars) + + string + + createPadding(nativeCeil(mid), chars) + ); + } + + /** + * Pads `string` on the right side if it's shorter than `length`. Padding + * characters are truncated if they exceed `length`. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category String + * @param {string} [string=''] The string to pad. + * @param {number} [length=0] The padding length. + * @param {string} [chars=' '] The string used as padding. + * @returns {string} Returns the padded string. + * @example + * + * _.padEnd('abc', 6); + * // => 'abc ' + * + * _.padEnd('abc', 6, '_-'); + * // => 'abc_-_' + * + * _.padEnd('abc', 3); + * // => 'abc' + */ + function padEnd(string, length, chars) { + string = toString(string); + length = toInteger(length); + + var strLength = length ? stringSize(string) : 0; + return (length && strLength < length) + ? (string + createPadding(length - strLength, chars)) + : string; + } + + /** + * Pads `string` on the left side if it's shorter than `length`. Padding + * characters are truncated if they exceed `length`. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category String + * @param {string} [string=''] The string to pad. + * @param {number} [length=0] The padding length. + * @param {string} [chars=' '] The string used as padding. + * @returns {string} Returns the padded string. + * @example + * + * _.padStart('abc', 6); + * // => ' abc' + * + * _.padStart('abc', 6, '_-'); + * // => '_-_abc' + * + * _.padStart('abc', 3); + * // => 'abc' + */ + function padStart(string, length, chars) { + string = toString(string); + length = toInteger(length); + + var strLength = length ? stringSize(string) : 0; + return (length && strLength < length) + ? (createPadding(length - strLength, chars) + string) + : string; + } + + /** + * Converts `string` to an integer of the specified radix. If `radix` is + * `undefined` or `0`, a `radix` of `10` is used unless `value` is a + * hexadecimal, in which case a `radix` of `16` is used. + * + * **Note:** This method aligns with the + * [ES5 implementation](https://es5.github.io/#x15.1.2.2) of `parseInt`. + * + * @static + * @memberOf _ + * @since 1.1.0 + * @category String + * @param {string} string The string to convert. + * @param {number} [radix=10] The radix to interpret `value` by. + * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`. + * @returns {number} Returns the converted integer. + * @example + * + * _.parseInt('08'); + * // => 8 + * + * _.map(['6', '08', '10'], _.parseInt); + * // => [6, 8, 10] + */ + function parseInt(string, radix, guard) { + if (guard || radix == null) { + radix = 0; + } else if (radix) { + radix = +radix; + } + return nativeParseInt(toString(string).replace(reTrimStart, ''), radix || 0); + } + + /** + * Repeats the given string `n` times. + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category String + * @param {string} [string=''] The string to repeat. + * @param {number} [n=1] The number of times to repeat the string. + * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`. + * @returns {string} Returns the repeated string. + * @example + * + * _.repeat('*', 3); + * // => '***' + * + * _.repeat('abc', 2); + * // => 'abcabc' + * + * _.repeat('abc', 0); + * // => '' + */ + function repeat(string, n, guard) { + if ((guard ? isIterateeCall(string, n, guard) : n === undefined)) { + n = 1; + } else { + n = toInteger(n); + } + return baseRepeat(toString(string), n); + } + + /** + * Replaces matches for `pattern` in `string` with `replacement`. + * + * **Note:** This method is based on + * [`String#replace`](https://mdn.io/String/replace). + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category String + * @param {string} [string=''] The string to modify. + * @param {RegExp|string} pattern The pattern to replace. + * @param {Function|string} replacement The match replacement. + * @returns {string} Returns the modified string. + * @example + * + * _.replace('Hi Fred', 'Fred', 'Barney'); + * // => 'Hi Barney' + */ + function replace() { + var args = arguments, + string = toString(args[0]); + + return args.length < 3 ? string : string.replace(args[1], args[2]); + } + + /** + * Converts `string` to + * [snake case](https://en.wikipedia.org/wiki/Snake_case). + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category String + * @param {string} [string=''] The string to convert. + * @returns {string} Returns the snake cased string. + * @example + * + * _.snakeCase('Foo Bar'); + * // => 'foo_bar' + * + * _.snakeCase('fooBar'); + * // => 'foo_bar' + * + * _.snakeCase('--FOO-BAR--'); + * // => 'foo_bar' + */ + var snakeCase = createCompounder(function(result, word, index) { + return result + (index ? '_' : '') + word.toLowerCase(); + }); + + /** + * Splits `string` by `separator`. + * + * **Note:** This method is based on + * [`String#split`](https://mdn.io/String/split). + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category String + * @param {string} [string=''] The string to split. + * @param {RegExp|string} separator The separator pattern to split by. + * @param {number} [limit] The length to truncate results to. + * @returns {Array} Returns the string segments. + * @example + * + * _.split('a-b-c', '-', 2); + * // => ['a', 'b'] + */ + function split(string, separator, limit) { + if (limit && typeof limit != 'number' && isIterateeCall(string, separator, limit)) { + separator = limit = undefined; + } + limit = limit === undefined ? MAX_ARRAY_LENGTH : limit >>> 0; + if (!limit) { + return []; + } + string = toString(string); + if (string && ( + typeof separator == 'string' || + (separator != null && !isRegExp(separator)) + )) { + separator = baseToString(separator); + if (!separator && hasUnicode(string)) { + return castSlice(stringToArray(string), 0, limit); + } + } + return string.split(separator, limit); + } + + /** + * Converts `string` to + * [start case](https://en.wikipedia.org/wiki/Letter_case#Stylistic_or_specialised_usage). + * + * @static + * @memberOf _ + * @since 3.1.0 + * @category String + * @param {string} [string=''] The string to convert. + * @returns {string} Returns the start cased string. + * @example + * + * _.startCase('--foo-bar--'); + * // => 'Foo Bar' + * + * _.startCase('fooBar'); + * // => 'Foo Bar' + * + * _.startCase('__FOO_BAR__'); + * // => 'FOO BAR' + */ + var startCase = createCompounder(function(result, word, index) { + return result + (index ? ' ' : '') + upperFirst(word); + }); + + /** + * Checks if `string` starts with the given target string. + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category String + * @param {string} [string=''] The string to inspect. + * @param {string} [target] The string to search for. + * @param {number} [position=0] The position to search from. + * @returns {boolean} Returns `true` if `string` starts with `target`, + * else `false`. + * @example + * + * _.startsWith('abc', 'a'); + * // => true + * + * _.startsWith('abc', 'b'); + * // => false + * + * _.startsWith('abc', 'b', 1); + * // => true + */ + function startsWith(string, target, position) { + string = toString(string); + position = position == null + ? 0 + : baseClamp(toInteger(position), 0, string.length); + + target = baseToString(target); + return string.slice(position, position + target.length) == target; + } + + /** + * Creates a compiled template function that can interpolate data properties + * in "interpolate" delimiters, HTML-escape interpolated data properties in + * "escape" delimiters, and execute JavaScript in "evaluate" delimiters. Data + * properties may be accessed as free variables in the template. If a setting + * object is given, it takes precedence over `_.templateSettings` values. + * + * **Note:** In the development build `_.template` utilizes + * [sourceURLs](http://www.html5rocks.com/en/tutorials/developertools/sourcemaps/#toc-sourceurl) + * for easier debugging. + * + * For more information on precompiling templates see + * [lodash's custom builds documentation](https://lodash.com/custom-builds). + * + * For more information on Chrome extension sandboxes see + * [Chrome's extensions documentation](https://developer.chrome.com/extensions/sandboxingEval). + * + * @static + * @since 0.1.0 + * @memberOf _ + * @category String + * @param {string} [string=''] The template string. + * @param {Object} [options={}] The options object. + * @param {RegExp} [options.escape=_.templateSettings.escape] + * The HTML "escape" delimiter. + * @param {RegExp} [options.evaluate=_.templateSettings.evaluate] + * The "evaluate" delimiter. + * @param {Object} [options.imports=_.templateSettings.imports] + * An object to import into the template as free variables. + * @param {RegExp} [options.interpolate=_.templateSettings.interpolate] + * The "interpolate" delimiter. + * @param {string} [options.sourceURL='lodash.templateSources[n]'] + * The sourceURL of the compiled template. + * @param {string} [options.variable='obj'] + * The data object variable name. + * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`. + * @returns {Function} Returns the compiled template function. + * @example + * + * // Use the "interpolate" delimiter to create a compiled template. + * var compiled = _.template('hello <%= user %>!'); + * compiled({ 'user': 'fred' }); + * // => 'hello fred!' + * + * // Use the HTML "escape" delimiter to escape data property values. + * var compiled = _.template('<%- value %>'); + * compiled({ 'value': '